Send our own reliable packet for a more accurate ping

This commit is contained in:
Benau 2018-07-03 14:20:35 +08:00
parent de8730dbfc
commit a44ce60991
6 changed files with 87 additions and 36 deletions

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -1366,6 +1366,7 @@ void ServerLobby::handleUnencryptedConnection(std::shared_ptr<STKPeer> 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<STKPeer> 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<NetworkPlayerProfile> npp : peer->getPlayerProfiles())
{

View File

@ -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

View File

@ -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<LobbyProtocol> 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<uint8_t, 5> 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<std::mutex> 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);