Add network timer synchronizer
This commit is contained in:
parent
7b646dd2cb
commit
35b167a824
79
src/network/network_timer_synchronizer.hpp
Normal file
79
src/network/network_timer_synchronizer.hpp
Normal file
@ -0,0 +1,79 @@
|
||||
//
|
||||
// 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 HEADER_NETWORK_TIMER_SYNCHRONIZER_HPP
|
||||
#define HEADER_NETWORK_TIMER_SYNCHRONIZER_HPP
|
||||
|
||||
#include "config/user_config.hpp"
|
||||
#include "utils/log.hpp"
|
||||
#include "utils/time.hpp"
|
||||
#include "utils/types.hpp"
|
||||
#include "main_loop.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <deque>
|
||||
#include <numeric>
|
||||
#include <tuple>
|
||||
|
||||
class NetworkTimerSynchronizer
|
||||
{
|
||||
private:
|
||||
std::deque<std::tuple<uint32_t, uint64_t, uint64_t> > m_times;
|
||||
|
||||
bool m_synchronised = false;
|
||||
public:
|
||||
// ------------------------------------------------------------------------
|
||||
bool isSynchronised() const { return m_synchronised; }
|
||||
// ------------------------------------------------------------------------
|
||||
void addAndSetTime(uint32_t ping, uint64_t server_time)
|
||||
{
|
||||
if (m_synchronised)
|
||||
return;
|
||||
|
||||
const uint64_t cur_time = StkTime::getRealTimeMs();
|
||||
// Take max 20 averaged samples from m_times, the next addAndGetTime
|
||||
// is used to determine that server_time if it's correct, if not
|
||||
// clear half in m_times until it's correct
|
||||
if (m_times.size() >= 20)
|
||||
{
|
||||
uint64_t sum = std::accumulate(m_times.begin(), m_times.end(),
|
||||
(uint64_t)0, [cur_time](const uint64_t previous,
|
||||
const std::tuple<uint32_t, uint64_t, uint64_t>& b)->uint64_t
|
||||
{
|
||||
return previous + (uint64_t)(std::get<0>(b) / 2) +
|
||||
std::get<1>(b) + cur_time - std::get<2>(b);
|
||||
});
|
||||
const int64_t averaged_time = sum / 20;
|
||||
const int64_t server_time_now = server_time + (uint64_t)(ping / 2);
|
||||
int difference = (int)std::abs(averaged_time - server_time_now);
|
||||
if (std::abs(averaged_time - server_time_now) <
|
||||
UserConfigParams::m_timer_sync_tolerance)
|
||||
{
|
||||
main_loop->setNetworkTimer(averaged_time);
|
||||
m_synchronised = true;
|
||||
Log::info("NetworkTimerSynchronizer", "Network "
|
||||
"timer synchronized, difference: %dms", difference);
|
||||
return;
|
||||
}
|
||||
m_times.erase(m_times.begin(), m_times.begin() + 10);
|
||||
}
|
||||
m_times.emplace_back(ping, server_time, cur_time);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // HEADER_NETWORK_TIMER_SYNCHRONIZER_HPP
|
@ -26,6 +26,7 @@
|
||||
#include "network/network_config.hpp"
|
||||
#include "network/network_console.hpp"
|
||||
#include "network/network_string.hpp"
|
||||
#include "network/network_timer_synchronizer.hpp"
|
||||
#include "network/protocols/connect_to_peer.hpp"
|
||||
#include "network/protocols/server_lobby.hpp"
|
||||
#include "network/protocol_manager.hpp"
|
||||
@ -34,6 +35,7 @@
|
||||
#include "utils/separate_process.hpp"
|
||||
#include "utils/time.hpp"
|
||||
#include "utils/vs.hpp"
|
||||
#include "main_loop.hpp"
|
||||
|
||||
#include <string.h>
|
||||
#if defined(WIN32)
|
||||
@ -283,6 +285,7 @@ STKHost::STKHost(bool server)
|
||||
/*channel_limit*/EVENT_CHANNEL_COUNT,
|
||||
/*max_in_bandwidth*/0, /*max_out_bandwidth*/0, &addr,
|
||||
true/*change_port_if_bound*/);
|
||||
m_nts.reset(new NetworkTimerSynchronizer());
|
||||
}
|
||||
|
||||
if (!m_network)
|
||||
@ -306,6 +309,7 @@ void STKHost::init()
|
||||
m_network = NULL;
|
||||
m_exit_timeout.store(std::numeric_limits<double>::max());
|
||||
m_client_ping.store(0);
|
||||
main_loop->resetStartNetworkGameTimer();
|
||||
|
||||
// Start with initialising ENet
|
||||
// ============================
|
||||
@ -333,6 +337,7 @@ void STKHost::init()
|
||||
*/
|
||||
STKHost::~STKHost()
|
||||
{
|
||||
main_loop->resetStartNetworkGameTimer();
|
||||
requestShutdown();
|
||||
if (m_network_console.joinable())
|
||||
m_network_console.join();
|
||||
@ -774,6 +779,8 @@ void STKHost::mainLoop()
|
||||
if (need_ping)
|
||||
{
|
||||
BareNetworkString ping_packet;
|
||||
uint64_t network_timer = main_loop->getNetworkTimer();
|
||||
ping_packet.addUInt64(network_timer);
|
||||
ping_packet.addUInt8((uint8_t)m_peer_pings.getData().size());
|
||||
for (auto& p : m_peer_pings.getData())
|
||||
ping_packet.addUInt32(p.first).addUInt32(p.second);
|
||||
@ -904,26 +911,37 @@ void STKHost::mainLoop()
|
||||
auto& peer = m_peers.at(event.peer);
|
||||
if (isPingPacket(event.packet->data, event.packet->dataLength))
|
||||
{
|
||||
if (!is_server && need_ping_update)
|
||||
if (!is_server)
|
||||
{
|
||||
m_peer_pings.lock();
|
||||
m_peer_pings.getData().clear();
|
||||
BareNetworkString ping_packet((char*)event.packet->data,
|
||||
event.packet->dataLength);
|
||||
std::map<uint32_t, uint32_t> peer_pings;
|
||||
ping_packet.skip(g_ping_packet.size());
|
||||
uint64_t server_time = ping_packet.getUInt64();
|
||||
unsigned peer_size = ping_packet.getUInt8();
|
||||
for (unsigned i = 0; i < peer_size; i++)
|
||||
{
|
||||
unsigned host_id = ping_packet.getUInt32();
|
||||
unsigned ping = ping_packet.getUInt32();
|
||||
m_peer_pings.getData()[host_id] = ping;
|
||||
peer_pings[host_id] = ping;
|
||||
}
|
||||
m_client_ping.store(
|
||||
m_peer_pings.getData().find(m_host_id) !=
|
||||
m_peer_pings.getData().end() ?
|
||||
m_peer_pings.getData().at(m_host_id) : 0,
|
||||
std::memory_order_relaxed);
|
||||
const uint32_t client_ping =
|
||||
peer_pings.find(m_host_id) != peer_pings.end() ?
|
||||
peer_pings.at(m_host_id) : 0;
|
||||
|
||||
if (client_ping > 0)
|
||||
{
|
||||
assert(m_nts);
|
||||
m_nts->addAndSetTime(client_ping, server_time);
|
||||
}
|
||||
if (need_ping_update)
|
||||
{
|
||||
m_peer_pings.lock();
|
||||
std::swap(m_peer_pings.getData(), peer_pings);
|
||||
m_peer_pings.unlock();
|
||||
m_client_ping.store(client_ping,
|
||||
std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
enet_packet_destroy(event.packet);
|
||||
continue;
|
||||
|
@ -46,6 +46,7 @@
|
||||
class GameSetup;
|
||||
class LobbyProtocol;
|
||||
class NetworkPlayerProfile;
|
||||
class NetworkTimerSynchronizer;
|
||||
class Server;
|
||||
class ServerLobby;
|
||||
class SeparateProcess;
|
||||
@ -137,6 +138,8 @@ private:
|
||||
|
||||
std::atomic<uint32_t> m_client_ping;
|
||||
|
||||
std::unique_ptr<NetworkTimerSynchronizer> m_nts;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
STKHost(bool server);
|
||||
// ------------------------------------------------------------------------
|
||||
@ -312,6 +315,10 @@ public:
|
||||
// ------------------------------------------------------------------------
|
||||
uint32_t getClientPingToServer() const
|
||||
{ return m_client_ping.load(std::memory_order_relaxed); }
|
||||
// ------------------------------------------------------------------------
|
||||
NetworkTimerSynchronizer* getNetworkTimerSynchronizer() const
|
||||
{ return m_nts.get(); }
|
||||
|
||||
}; // class STKHost
|
||||
|
||||
#endif // STK_HOST_HPP
|
||||
|
Loading…
Reference in New Issue
Block a user