diff --git a/src/network/event.cpp b/src/network/event.cpp index ed1cfbd97..7cb29e86b 100644 --- a/src/network/event.cpp +++ b/src/network/event.cpp @@ -30,6 +30,7 @@ Event::Event(ENetEvent* event, std::shared_ptr peer) { m_arrival_time = (double)StkTime::getTimeSinceEpoch(); + m_pdi = PDI_TIMEOUT; switch (event->type) { @@ -38,6 +39,7 @@ Event::Event(ENetEvent* event, std::shared_ptr peer) break; case ENET_EVENT_TYPE_DISCONNECT: m_type = EVENT_TYPE_DISCONNECTED; + m_pdi = (PeerDisconnectInfo)event->data; break; case ENET_EVENT_TYPE_RECEIVE: m_type = EVENT_TYPE_MESSAGE; diff --git a/src/network/event.hpp b/src/network/event.hpp index d3ecc42ef..0b1d5be5c 100644 --- a/src/network/event.hpp +++ b/src/network/event.hpp @@ -44,6 +44,7 @@ enum EVENT_TYPE EVENT_TYPE_DISCONNECTED,//!< A peer is disconnected EVENT_TYPE_MESSAGE //!< A message between server and client protocols }; +enum PeerDisconnectInfo : unsigned int; /*! * \class Event @@ -72,6 +73,9 @@ private: /** Arrivial time of the event, for timeouts. */ double m_arrival_time; + /** For disconnection event, a bit more info is provided. */ + PeerDisconnectInfo m_pdi; + public: Event(ENetEvent* event, std::shared_ptr peer); ~Event(); @@ -103,7 +107,8 @@ public: // ------------------------------------------------------------------------ /** Returns the arrival time of this event. */ double getArrivalTime() const { return m_arrival_time; } - + // ------------------------------------------------------------------------ + PeerDisconnectInfo getPeerDisconnectInfo() const { return m_pdi; } // ------------------------------------------------------------------------ }; // class Event diff --git a/src/network/network_console.cpp b/src/network/network_console.cpp index fad043994..593197e9c 100644 --- a/src/network/network_console.cpp +++ b/src/network/network_console.cpp @@ -37,7 +37,7 @@ void kickAllPlayers(STKHost* host) auto peers = host->getPeers(); for (unsigned int i = 0; i < peers.size(); i++) { - peers[i]->disconnect(); + peers[i]->kick(); } } // kickAllPlayers diff --git a/src/network/protocols/client_lobby.cpp b/src/network/protocols/client_lobby.cpp index 2a414c5ac..ee5256cdf 100644 --- a/src/network/protocols/client_lobby.cpp +++ b/src/network/protocols/client_lobby.cpp @@ -61,7 +61,6 @@ ClientLobby::ClientLobby() : LobbyProtocol(NULL) { m_server_address.clear(); - m_server = NULL; setHandleDisconnections(true); } // ClientLobby @@ -207,8 +206,6 @@ void ClientLobby::voteLaps(uint8_t player_id, uint8_t laps, */ void ClientLobby::leave() { - m_server->disconnect(); - m_server_address.clear(); } // leave //----------------------------------------------------------------------------- @@ -288,6 +285,22 @@ bool ClientLobby::notifyEventAsynchronous(Event* event) // the ProtocolManager, which might already have been deleted. // So only signal that STKHost should exit, which will be tested // from the main thread. + STKHost::get()->disconnectAllPeers(false/*timeout_waiting*/); + switch(event->getPeerDisconnectInfo()) + { + case PDI_TIMEOUT: + STKHost::get()->setErrorMessage( + _("Server connection timed out.")); + break; + case PDI_NORMAL: + STKHost::get()->setErrorMessage( + _("Server has been shut down.")); + break; + case PDI_KICK: + STKHost::get()->setErrorMessage( + _("You were kicked from the server.")); + break; + } // switch STKHost::get()->requestShutdown(); return true; } // disconnection @@ -507,8 +520,8 @@ void ClientLobby::connectionAccepted(Event* event) uint8_t player_id = data.getUInt8(); uint8_t host_id = data.getUInt8(); irr::core::stringw name; - int bytes_read = data.decodeStringW(&name); - + data.decodeStringW(&name); + NetworkPlayerProfile* profile2 = new NetworkPlayerProfile(name, player_id, host_id); m_game_setup->addPlayer(profile2); @@ -521,7 +534,6 @@ void ClientLobby::connectionAccepted(Event* event) // on server and all clients. m_game_setup->addPlayer(profile); NetworkingLobby::getInstance()->addPlayer(profile); - m_server = event->getPeer(); m_state = CONNECTED; if (NetworkConfig::get()->isAutoConnect()) { @@ -841,7 +853,7 @@ void ClientLobby::playerTrackVote(Event* event) std::string track_name; uint8_t player_id = data.getUInt8(); uint8_t number = data.getUInt8(); - int N = data.decodeString(&track_name); + data.decodeString(&track_name); m_game_setup->getRaceConfig()->setPlayerTrackVote(player_id, track_name, number); } // playerTrackVote diff --git a/src/network/protocols/client_lobby.hpp b/src/network/protocols/client_lobby.hpp index bd7b5525a..253c0cf99 100644 --- a/src/network/protocols/client_lobby.hpp +++ b/src/network/protocols/client_lobby.hpp @@ -6,8 +6,6 @@ #include "utils/cpp2011.hpp" #include -class STKPeer; - class ClientLobby : public LobbyProtocol { private: @@ -31,8 +29,6 @@ private: TransportAddress m_server_address; - STKPeer* m_server; - enum STATE { NONE, diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index e33255b3c..336cc1d34 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -59,6 +59,7 @@ #include #include +#include #include #include @@ -320,9 +321,7 @@ void STKHost::init() m_shutdown = false; m_network = NULL; m_game_setup = NULL; - - m_exit_flag.clear(); - m_exit_flag.test_and_set(); + m_exit_timeout.store(std::numeric_limits::max()); // Start with initialising ENet // ============================ @@ -358,8 +357,7 @@ STKHost::~STKHost() delete m_game_setup; m_game_setup = NULL; - m_peers.clear(); - + disconnectAllPeers(true/*timeout_waiting*/); Network::closeLog(); stopListening(); @@ -385,7 +383,6 @@ STKHost::~STKHost() void STKHost::shutdown() { ProtocolManager::lock()->abort(); - deleteAllPeers(); destroy(); } // shutdown @@ -624,14 +621,21 @@ GameSetup* STKHost::setupNewGame() } // setupNewGame //----------------------------------------------------------------------------- -/** Called when you leave a server. +/** Disconnect all connected peers. */ -void STKHost::deleteAllPeers() +void STKHost::disconnectAllPeers(bool timeout_waiting) { - m_peers.clear(); -} // deleteAllPeers + std::lock_guard lock(m_peers_mutex); + if (!m_peers.empty()) + { + // Wait for at most 2 seconds for disconnect event to be generated + if (timeout_waiting) + m_exit_timeout.store(StkTime::getRealTime() + 2.0); + m_peers.clear(); + } +} // disconnectAllPeers -// -------------------------------------------------------------------- +//----------------------------------------------------------------------------- /** Sets an error message for the gui. */ void STKHost::setErrorMessage(const irr::core::stringw &message) @@ -644,7 +648,7 @@ void STKHost::setErrorMessage(const irr::core::stringw &message) m_error_message = message; } // setErrorMessage -// -------------------------------------------------------------------- +//----------------------------------------------------------------------------- /** \brief Try to establish a connection to a given transport address. * \param peer : The transport address which you want to connect to. * \return True if we're successfully connected. False elseway. @@ -672,8 +676,7 @@ bool STKHost::connect(const TransportAddress& address) */ void STKHost::startListening() { - m_exit_flag.clear(); - m_exit_flag.test_and_set(); + m_exit_timeout.store(std::numeric_limits::max()); m_listening_thread = std::thread(std::bind(&STKHost::mainLoop, this)); } // startListening @@ -683,7 +686,8 @@ void STKHost::startListening() */ void STKHost::stopListening() { - m_exit_flag.clear(); + if (m_exit_timeout.load() == std::numeric_limits::max()) + m_exit_timeout.store(0.0); if (m_listening_thread.joinable()) m_listening_thread.join(); } // stopListening @@ -736,7 +740,7 @@ void STKHost::mainLoop() } } - while (m_exit_flag.test_and_set()) + while (m_exit_timeout.load() > StkTime::getRealTime()) { auto sl = LobbyProtocol::get(); if (direct_socket && sl && sl->waitingForPlayers()) @@ -749,21 +753,14 @@ void STKHost::mainLoop() if (event.type == ENET_EVENT_TYPE_NONE) continue; - auto pm = ProtocolManager::lock(); - if (!pm || pm->isExiting()) - { - // Don't create more event if no protocol manager or it will - // be exiting - enet_packet_destroy(event.packet); - continue; - } - Event* stk_event = NULL; if (event.type == ENET_EVENT_TYPE_CONNECT) { auto stk_peer = std::make_shared(event.peer, m_network); + std::unique_lock lock(m_peers_mutex); m_peers[event.peer] = stk_peer; + lock.unlock(); stk_event = new Event(&event, stk_peer); TransportAddress addr(event.peer->address); Log::info("STKHost", "%s has just connected. There are " @@ -772,21 +769,36 @@ void STKHost::mainLoop() else if (event.type == ENET_EVENT_TYPE_DISCONNECT) { Log::flushBuffers(); + + // If used a timeout waiting disconnect, exit now + if (m_exit_timeout.load() != + std::numeric_limits::max()) + { + m_exit_timeout.store(0.0); + break; + } // Use the previous stk peer so protocol can see the network // profile and handle it for disconnection - assert(m_peers.find(event.peer) != m_peers.end()); - stk_event = new Event(&event, m_peers.at(event.peer)); - m_peers.erase(event.peer); + if (m_peers.find(event.peer) != m_peers.end()) + { + stk_event = new Event(&event, m_peers.at(event.peer)); + std::lock_guard lock(m_peers_mutex); + m_peers.erase(event.peer); + } TransportAddress addr(event.peer->address); Log::info("STKHost", "%s has just disconnected. There are " "now %u peers.", addr.toString().c_str(), getPeerCount()); } // ENET_EVENT_TYPE_DISCONNECT - if (!stk_event) + if (!stk_event && m_peers.find(event.peer) != m_peers.end()) { - assert(m_peers.find(event.peer) != m_peers.end()); stk_event = new Event(&event, m_peers.at(event.peer)); } + else if (!stk_event) + { + enet_packet_destroy(event.packet); + continue; + } if (stk_event->getType() == EVENT_TYPE_MESSAGE) { Network::logPacket(stk_event->data(), true); @@ -802,8 +814,11 @@ void STKHost::mainLoop() } // if message event // notify for the event now. - pm->propagateEvent(stk_event); - + auto pm = ProtocolManager::lock(); + if (pm && !pm->isExiting()) + pm->propagateEvent(stk_event); + else + delete stk_event; } // while enet_host_service } // while m_exit_flag.test_and_set() delete direct_socket; diff --git a/src/network/stk_host.hpp b/src/network/stk_host.hpp index 981dd8bae..9ef5d5b39 100644 --- a/src/network/stk_host.hpp +++ b/src/network/stk_host.hpp @@ -97,8 +97,8 @@ private: * triggers a shutdown of the STKHost (and the Protocolmanager). */ std::atomic_bool m_shutdown; - /** Atomic flag used to stop this thread. */ - std::atomic_flag m_exit_flag = ATOMIC_FLAG_INIT; + /** Use as a timeout to waiting a disconnect event when exiting. */ + std::atomic m_exit_timeout; /** An error message, which is set by a protocol to be displayed * in the GUI. */ @@ -162,10 +162,11 @@ public: // ------------------------------------------------------------------------ void setPublicAddress(); // ------------------------------------------------------------------------ - virtual GameSetup* setupNewGame(); - void deleteAllPeers(); + GameSetup* setupNewGame(); + // ------------------------------------------------------------------------ + void disconnectAllPeers(bool timeout_waiting = false); + // ------------------------------------------------------------------------ bool connect(const TransportAddress& peer); - //------------------------------------------------------------------------- /** Requests that the network infrastructure is to be shut down. This * function is called from a thread, but the actual shutdown needs to be diff --git a/src/network/stk_peer.cpp b/src/network/stk_peer.cpp index e646e2168..dc5470c06 100644 --- a/src/network/stk_peer.cpp +++ b/src/network/stk_peer.cpp @@ -44,17 +44,26 @@ STKPeer::STKPeer(ENetPeer *enet_peer, Network* network) */ STKPeer::~STKPeer() { - enet_peer_disconnect(m_enet_peer, 0); - m_client_server_token = 0; + TransportAddress a(m_enet_peer->address); + if (m_enet_peer->state != ENET_PEER_STATE_CONNECTED || + a != m_peer_address) + return; + auto lock = m_network->acquireEnetLock(); + enet_peer_disconnect(m_enet_peer, PDI_NORMAL); } // ~STKPeer //----------------------------------------------------------------------------- -/** Disconnect from the server. +/** Kick this peer (used by server). */ -void STKPeer::disconnect() +void STKPeer::kick() { - enet_peer_disconnect(m_enet_peer, 0); -} // disconnect + TransportAddress a(m_enet_peer->address); + if (m_enet_peer->state != ENET_PEER_STATE_CONNECTED || + a != m_peer_address) + return; + auto lock = m_network->acquireEnetLock(); + enet_peer_disconnect(m_enet_peer, PDI_KICK); +} // kick //----------------------------------------------------------------------------- /** Sends a packet to this host. @@ -86,7 +95,7 @@ void STKPeer::sendPacket(NetworkString *data, bool reliable) */ bool STKPeer::isConnected() const { - Log::info("STKPeer", "The peer state is %i", m_enet_peer->state); + Log::debug("STKPeer", "The peer state is %i", m_enet_peer->state); return (m_enet_peer->state == ENET_PEER_STATE_CONNECTED); } // isConnected diff --git a/src/network/stk_peer.hpp b/src/network/stk_peer.hpp index bc3625c74..ed142c67b 100644 --- a/src/network/stk_peer.hpp +++ b/src/network/stk_peer.hpp @@ -36,6 +36,13 @@ class NetworkPlayerProfile; class NetworkString; class TransportAddress; +enum PeerDisconnectInfo : unsigned int +{ + PDI_TIMEOUT = 0, //!< Timeout disconnected (default in enet). + PDI_NORMAL = 1, //!< Normal disconnction with acknowledgement + PDI_KICK = 2, //!< Kick disconnection +}; // PeerDisconnectInfo + /*! \class STKPeer * \brief Represents a peer. * This class is used to interface the ENetPeer structure. @@ -64,11 +71,12 @@ protected: public: STKPeer(ENetPeer *enet_peer, Network* network); - virtual ~STKPeer(); - - virtual void sendPacket(NetworkString *data, - bool reliable = true); - void disconnect(); + ~STKPeer(); + // ------------------------------------------------------------------------ + void sendPacket(NetworkString *data, bool reliable = true); + // ------------------------------------------------------------------------ + void kick(); + // ------------------------------------------------------------------------ bool isConnected() const; const TransportAddress& getAddress() const { return m_peer_address; } bool isSamePeer(const STKPeer* peer) const; @@ -79,7 +87,7 @@ public: void setClientServerToken(const uint32_t& token) { m_client_server_token = token; - m_token_set = true; + m_token_set = true; } // setClientServerToken // ------------------------------------------------------------------------ void unsetClientServerToken() { m_token_set = false; }