diff --git a/src/network/protocols/client_lobby.cpp b/src/network/protocols/client_lobby.cpp index e6e1853f5..be570626e 100644 --- a/src/network/protocols/client_lobby.cpp +++ b/src/network/protocols/client_lobby.cpp @@ -57,7 +57,7 @@ digraph interaction { "REQUESTING_CONNECTION" -> "?? TO BE DONE ??" [label="Connection refused by server"] "CONNECTED" -> "KART_SELECTION" [label="Server tells us to start kart selection"] "KART_SELECTION" -> "SELECTING_KARTS" [label="Show kart selection screen"] -"SELECTING_KARTS" -> "PLAYING" [label="Server sends start race message"] +"SELECTING_KARTS" -> "RACING" [label="Server sends start race message"] } \enddot Note that some states are actually managed outside of the client lobby. For @@ -313,14 +313,6 @@ void ClientLobby::update(int ticks) m_state.store(REQUESTING_CONNECTION); } break; - case REQUESTING_CONNECTION: - break; - case CONNECTED: - break; - case SELECTING_ASSETS: - break; - case PLAYING: - break; case RACE_FINISHED: if (!RaceEventManager::getInstance()->protocolStopped() || !GameProtocol::emptyInstance()) @@ -332,6 +324,10 @@ void ClientLobby::update(int ticks) m_state.store(EXITING); requestTerminate(); break; + case REQUESTING_CONNECTION: + case CONNECTED: + case SELECTING_ASSETS: + case RACING: case EXITING: break; } @@ -656,7 +652,7 @@ void ClientLobby::connectionRefused(Event* event) */ void ClientLobby::startGame(Event* event) { - m_state.store(PLAYING); + m_state.store(RACING); // Triggers the world finite state machine to go from WAIT_FOR_SERVER_PHASE // to READY_PHASE. World::getWorld()->setReadyToRace(); diff --git a/src/network/protocols/client_lobby.hpp b/src/network/protocols/client_lobby.hpp index f516e38d6..9efc4c801 100644 --- a/src/network/protocols/client_lobby.hpp +++ b/src/network/protocols/client_lobby.hpp @@ -1,3 +1,21 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2018 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #ifndef CLIENT_LOBBY_HPP #define CLIENT_LOBBY_HPP @@ -42,7 +60,7 @@ private: REQUESTING_CONNECTION, CONNECTED, // means in the lobby room SELECTING_ASSETS, // in the kart selection or tracks screen - PLAYING, // racing + RACING, // racing RACE_FINISHED, // race result shown DONE, EXITING @@ -79,9 +97,11 @@ public: { return m_state.load() == CONNECTED; } virtual void asynchronousUpdate() OVERRIDE {} virtual bool allPlayersReady() const OVERRIDE - { return m_state.load() >= PLAYING; } + { return m_state.load() >= RACING; } bool waitingForServerRespond() const { return m_state.load() == REQUESTING_CONNECTION; } + virtual bool isRacing() const OVERRIDE { return m_state.load() == RACING; } + }; #endif // CLIENT_LOBBY_HPP diff --git a/src/network/protocols/lobby_protocol.hpp b/src/network/protocols/lobby_protocol.hpp index 71c68073f..5ae750fb8 100644 --- a/src/network/protocols/lobby_protocol.hpp +++ b/src/network/protocols/lobby_protocol.hpp @@ -116,6 +116,7 @@ public: virtual void loadWorld(); virtual bool waitingForPlayers() const = 0; virtual bool allPlayersReady() const = 0; + virtual bool isRacing() const = 0; GameSetup* getGameSetup() const { return m_game_setup; } }; // class LobbyProtocol diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 4282fec81..d33c075a6 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -1366,6 +1366,7 @@ void ServerLobby::handleUnencryptedConnection(std::shared_ptr peer, server_info->addUInt8(LE_SERVER_INFO); m_game_setup->addServerInfo(server_info); peer->sendPacket(server_info); + delete server_info; NetworkString* message_ack = getNetworkString(4); message_ack->setSynchronous(true); @@ -1377,12 +1378,6 @@ void ServerLobby::handleUnencryptedConnection(std::shared_ptr peer, peer->sendPacket(message_ack); delete message_ack; - // Make sure it will always ping at least the frequency of state exchange - // so enet will not ping when we exchange state but keep ping elsewhere - // then in lobby the ping seen will be correct - peer->setPingInterval(110); - delete server_info; - m_peers_ready[peer] = std::make_pair(false, 0.0); for (std::shared_ptr npp : peer->getPlayerProfiles()) { diff --git a/src/network/protocols/server_lobby.hpp b/src/network/protocols/server_lobby.hpp index df4de3457..0d29d2cf4 100644 --- a/src/network/protocols/server_lobby.hpp +++ b/src/network/protocols/server_lobby.hpp @@ -1,3 +1,21 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2018 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #ifndef SERVER_LOBBY_HPP #define SERVER_LOBBY_HPP @@ -219,6 +237,7 @@ public: virtual bool waitingForPlayers() const OVERRIDE; virtual bool allPlayersReady() const OVERRIDE { return m_state.load() >= WAIT_FOR_RACE_STARTED; } + virtual bool isRacing() const OVERRIDE { return m_state.load() == RACING; } }; // class ServerLobby diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index bd1bc36d2..370ebc5e7 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -18,6 +18,7 @@ #include "network/stk_host.hpp" +#include "config/stk_config.hpp" #include "config/user_config.hpp" #include "io/file_manager.hpp" #include "network/event.hpp" @@ -241,23 +242,19 @@ std::shared_ptr STKHost::create(SeparateProcess* p) * LocalPlayerController for each kart. Each remote player gets a * NULL ActivePlayer (the ActivePlayer is only used for assigning the input * device to each kart, achievements and highscores, so it's not needed for - * remote players). It will also start the LatencyProtocol, - * RaceEventManager and then load the world. - - * TODO: - * Once the server has received all - * messages in notifyEventAsynchronous(), it will call startCountdown() - * in the LatencyProtocol. The LatencyProtocol is - * sending regular (once per second) pings to the clients and measure - * the averate latency. Upon starting the countdown this information - * is included in the ping request, so the clients can start the countdown - * at that stage as well. - * - * Once the countdown is 0 (or below), the Synchronization Protocol will - * start the protocols: KartUpdateProtocol, GameProtocol, - * GameEventsProtocol. Then the LatencyProtocol is terminated - * which indicates to the main loop to start the actual game. + * remote players). It will also start the RaceEventManager and then load the + * world. */ +// ============================================================================ +constexpr std::array g_ping_packet {{ 255, 'p', 'i', 'n', 'g' }}; + +// ============================================================================ +constexpr bool isPingPacket(unsigned char* data, size_t length) +{ + return length == g_ping_packet.size() && data[0] == g_ping_packet[0] && + data[1] == g_ping_packet[1] && data[2] == g_ping_packet[2] && + data[3] == g_ping_packet[3] && data[4] == g_ping_packet[4]; +} // isPingPacket // ============================================================================ /** The constructor for a server or client. @@ -715,6 +712,7 @@ void STKHost::mainLoop() } } + double last_ping_time = StkTime::getRealTime(); while (m_exit_timeout.load() > StkTime::getRealTime()) { if (!is_server) @@ -737,11 +735,28 @@ void STKHost::mainLoop() if (is_server) { std::unique_lock peer_lock(m_peers_mutex); - // Remove peer which has not been validated after a specific time - // It is validated when the first connection request has finished const float timeout = UserConfigParams::m_validation_timeout; + bool need_ping = false; + if (sl && !sl->isRacing() && + last_ping_time < StkTime::getRealTime()) + { + // If not racing, send an reliable packet at the same rate with + // state exchange to keep enet ping accurate + last_ping_time = StkTime::getRealTime() + + 1.0 / double(stk_config->m_network_state_frequeny); + need_ping = true; + } for (auto it = m_peers.begin(); it != m_peers.end();) { + if (need_ping) + { + ENetPacket* packet = enet_packet_create(g_ping_packet.data(), + g_ping_packet.size(), ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(it->first, EVENT_CHANNEL_UNENCRYPTED, packet); + } + + // Remove peer which has not been validated after a specific time + // It is validated when the first connection request has finished if (!it->second->isValidated() && (float)StkTime::getRealTime() > it->second->getConnectedTime() + timeout) @@ -837,6 +852,11 @@ void STKHost::mainLoop() if (!stk_event && m_peers.find(event.peer) != m_peers.end()) { auto& peer = m_peers.at(event.peer); + if (isPingPacket(event.packet->data, event.packet->dataLength)) + { + enet_packet_destroy(event.packet); + continue; + } try { stk_event = new Event(&event, peer);