Use the network timer synchronizer to start game
This commit is contained in:
@@ -66,8 +66,6 @@ LRESULT CALLBACK separateProcessProc(_In_ HWND hwnd, _In_ UINT uMsg,
|
||||
MainLoop::MainLoop(unsigned parent_pid)
|
||||
: m_abort(false), m_ticks_adjustment(0), m_parent_pid(parent_pid)
|
||||
{
|
||||
m_network_timer.store(StkTime::getRealTimeMs());
|
||||
m_start_game_ticks.store(0);
|
||||
m_curr_time = 0;
|
||||
m_prev_time = 0;
|
||||
m_throttle_fps = true;
|
||||
@@ -495,18 +493,4 @@ void MainLoop::abort()
|
||||
m_abort = true;
|
||||
} // abort
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Set game start ticks told by server somewhere in the future.
|
||||
*/
|
||||
void MainLoop::setStartNetworkGameTimer(uint64_t ticks)
|
||||
{
|
||||
uint64_t ticks_now = getNetworkTimer();
|
||||
if (ticks < ticks_now)
|
||||
{
|
||||
Log::warn("MainLoop", "Network timer is too slow to catch up");
|
||||
ticks = ticks_now;
|
||||
}
|
||||
m_start_game_ticks.store(ticks);
|
||||
} // setStartNetworkGameTimer
|
||||
|
||||
/* EOF */
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#define HEADER_MAIN_LOOP_HPP
|
||||
|
||||
#include "utils/synchronised.hpp"
|
||||
#include "utils/time.hpp"
|
||||
#include "utils/types.hpp"
|
||||
#include <atomic>
|
||||
|
||||
@@ -40,8 +39,6 @@ private:
|
||||
|
||||
Synchronised<int> m_ticks_adjustment;
|
||||
|
||||
std::atomic<uint64_t> m_network_timer, m_start_game_ticks;
|
||||
|
||||
uint32_t m_curr_time;
|
||||
uint32_t m_prev_time;
|
||||
unsigned m_parent_pid;
|
||||
@@ -65,17 +62,6 @@ public:
|
||||
m_ticks_adjustment.getData() += ticks;
|
||||
m_ticks_adjustment.unlock();
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
uint64_t getNetworkTimer() const
|
||||
{ return StkTime::getRealTimeMs() - m_network_timer.load(); }
|
||||
// ------------------------------------------------------------------------
|
||||
void setNetworkTimer(uint64_t ticks)
|
||||
{ m_network_timer.store(StkTime::getRealTimeMs() - ticks); }
|
||||
// ------------------------------------------------------------------------
|
||||
void resetStartNetworkGameTimer() { m_start_game_ticks.store(0); }
|
||||
// ------------------------------------------------------------------------
|
||||
void setStartNetworkGameTimer(uint64_t ticks);
|
||||
|
||||
}; // MainLoop
|
||||
|
||||
extern MainLoop* main_loop;
|
||||
|
||||
@@ -49,7 +49,6 @@ WorldStatus::WorldStatus()
|
||||
m_play_track_intro_sound = UserConfigParams::m_music;
|
||||
m_play_ready_set_go_sounds = true;
|
||||
m_play_racestart_sounds = true;
|
||||
m_server_is_ready.store(false);
|
||||
|
||||
IrrlichtDevice *device = irr_driver->getDevice();
|
||||
|
||||
@@ -96,10 +95,6 @@ void WorldStatus::reset()
|
||||
|
||||
// Set the right music
|
||||
Track::getCurrentTrack()->startMusic();
|
||||
// In case of a networked race the race can only start once
|
||||
// all protocols are up. This flag is used to wait for
|
||||
// a confirmation before starting the actual race.
|
||||
m_server_is_ready.store(false);
|
||||
} // reset
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -271,25 +266,13 @@ void WorldStatus::updateTime(int ticks)
|
||||
return; // Don't increase time
|
||||
case WAIT_FOR_SERVER_PHASE:
|
||||
{
|
||||
// Wait for all players to finish loading world
|
||||
auto lobby = LobbyProtocol::get<LobbyProtocol>();
|
||||
assert(lobby);
|
||||
if (!lobby->allPlayersReady())
|
||||
return;
|
||||
// This stage is only reached in case of a networked game.
|
||||
// The server waits for a confirmation from
|
||||
// each client that they have started (to guarantee that the
|
||||
// server is running with a local time behind all clients).
|
||||
if (m_play_ready_set_go_sounds)
|
||||
m_prestart_sound->play();
|
||||
|
||||
if (NetworkConfig::get()->isServer() &&
|
||||
m_server_is_ready.load() == false) return;
|
||||
|
||||
m_phase = READY_PHASE;
|
||||
auto cl = LobbyProtocol::get<ClientLobby>();
|
||||
if (cl)
|
||||
cl->startingRaceNow();
|
||||
if (lobby && lobby->isRacing())
|
||||
{
|
||||
if (m_play_ready_set_go_sounds)
|
||||
m_prestart_sound->play();
|
||||
m_phase = READY_PHASE;
|
||||
}
|
||||
return; // Don't increase time
|
||||
}
|
||||
case READY_PHASE:
|
||||
@@ -463,19 +446,6 @@ void WorldStatus::updateTime(int ticks)
|
||||
} // switch m_phase
|
||||
} // update
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Called on the client when it receives a notification from the server that
|
||||
* all clients (and server) are ready to start the race. The server will
|
||||
* then additionally wait for all clients to report back that they are
|
||||
* starting, which guarantees that the server is running far enough behind
|
||||
* clients time that at server time T all events from the clients at time
|
||||
* T have arrived, minimising rollback impact.
|
||||
*/
|
||||
void WorldStatus::startReadySetGo()
|
||||
{
|
||||
m_server_is_ready.store(true);
|
||||
} // startReadySetGo
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Sets the time for the clock.
|
||||
* \param time New time to set.
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#define HEADER_WORLD_STATUS_HPP
|
||||
|
||||
#include "utils/cpp2011.hpp"
|
||||
#include <atomic>
|
||||
|
||||
class SFXBase;
|
||||
|
||||
@@ -132,11 +131,6 @@ private:
|
||||
int m_count_up_ticks;
|
||||
|
||||
bool m_engines_started;
|
||||
/** In networked game a client must wait for the server to start 'ready
|
||||
* set go' to make sure all client are actually ready to start the game.
|
||||
* A server on the other hand will run behind all clients, so it will
|
||||
* wait for all clients to indicate that they have started the race. */
|
||||
std::atomic_bool m_server_is_ready;
|
||||
|
||||
void startEngines();
|
||||
|
||||
@@ -207,8 +201,6 @@ public:
|
||||
// ------------------------------------------------------------------------
|
||||
/** Get the ticks since start regardless of which way the clock counts */
|
||||
int getTicksSinceStart() const { return m_count_up_ticks; }
|
||||
// ------------------------------------------------------------------------
|
||||
void setReadyToRace() { m_server_is_ready.store(true); }
|
||||
}; // WorldStatus
|
||||
|
||||
|
||||
|
||||
@@ -20,11 +20,12 @@
|
||||
#define HEADER_NETWORK_TIMER_SYNCHRONIZER_HPP
|
||||
|
||||
#include "config/user_config.hpp"
|
||||
#include "network/stk_host.hpp"
|
||||
#include "utils/log.hpp"
|
||||
#include "utils/time.hpp"
|
||||
#include "utils/types.hpp"
|
||||
#include "main_loop.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdlib>
|
||||
#include <deque>
|
||||
#include <numeric>
|
||||
@@ -35,16 +36,37 @@ class NetworkTimerSynchronizer
|
||||
private:
|
||||
std::deque<std::tuple<uint32_t, uint64_t, uint64_t> > m_times;
|
||||
|
||||
bool m_synchronised = false;
|
||||
std::atomic_bool m_synchronised, m_force_set_timer;
|
||||
|
||||
public:
|
||||
NetworkTimerSynchronizer()
|
||||
{
|
||||
m_synchronised.store(false);
|
||||
m_force_set_timer.store(false);
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
bool isSynchronised() const { return m_synchronised; }
|
||||
bool isSynchronised() const { return m_synchronised.load(); }
|
||||
// ------------------------------------------------------------------------
|
||||
void enableForceSetTimer()
|
||||
{
|
||||
if (m_synchronised.load() == true)
|
||||
return;
|
||||
m_force_set_timer.store(true);
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
void addAndSetTime(uint32_t ping, uint64_t server_time)
|
||||
{
|
||||
if (m_synchronised)
|
||||
if (m_synchronised.load() == true)
|
||||
return;
|
||||
|
||||
if (m_force_set_timer.load() == true)
|
||||
{
|
||||
m_force_set_timer.store(false);
|
||||
m_synchronised.store(true);
|
||||
STKHost::get()->setNetworkTimer(server_time + (uint64_t)(ping / 2));
|
||||
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
|
||||
@@ -64,8 +86,9 @@ public:
|
||||
if (std::abs(averaged_time - server_time_now) <
|
||||
UserConfigParams::m_timer_sync_tolerance)
|
||||
{
|
||||
main_loop->setNetworkTimer(averaged_time);
|
||||
m_synchronised = true;
|
||||
STKHost::get()->setNetworkTimer(averaged_time);
|
||||
m_force_set_timer.store(false);
|
||||
m_synchronised.store(true);
|
||||
Log::info("NetworkTimerSynchronizer", "Network "
|
||||
"timer synchronized, difference: %dms", difference);
|
||||
return;
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "network/game_setup.hpp"
|
||||
#include "network/network_config.hpp"
|
||||
#include "network/network_player_profile.hpp"
|
||||
#include "network/network_timer_synchronizer.hpp"
|
||||
#include "network/protocols/game_protocol.hpp"
|
||||
#include "network/protocols/game_events_protocol.hpp"
|
||||
#include "network/race_event_manager.hpp"
|
||||
@@ -211,6 +212,18 @@ void ClientLobby::addAllPlayers(Event* event)
|
||||
STKHost::get()->requestShutdown();
|
||||
return;
|
||||
}
|
||||
// Timeout is too slow to synchronize, force it to stop and set current
|
||||
// time
|
||||
if (!STKHost::get()->getNetworkTimerSynchronizer()->isSynchronised())
|
||||
{
|
||||
core::stringw msg = _("Bad network connection is detected.");
|
||||
MessageQueue::add(MessageQueue::MT_ERROR, msg);
|
||||
Log::warn("ClientLobby", "Failed to synchronize timer before game "
|
||||
"start, maybe you enter the game too quick? (at least 5 seconds "
|
||||
"are required for synchronization.");
|
||||
STKHost::get()->getNetworkTimerSynchronizer()->enableForceSetTimer();
|
||||
}
|
||||
|
||||
NetworkString& data = event->data();
|
||||
std::string track_name;
|
||||
data.decodeString(&track_name);
|
||||
@@ -737,37 +750,31 @@ void ClientLobby::connectionRefused(Event* event)
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/*! \brief Called when the server broadcasts to start the race to all clients.
|
||||
* \param event : Event providing the information (no additional informati
|
||||
* in this case).
|
||||
* \param event : Event providing the time the client should start game.
|
||||
*/
|
||||
void ClientLobby::startGame(Event* event)
|
||||
{
|
||||
m_state.store(RACING);
|
||||
// Triggers the world finite state machine to go from WAIT_FOR_SERVER_PHASE
|
||||
// to READY_PHASE.
|
||||
World::getWorld()->setReadyToRace();
|
||||
Log::info("ClientLobby", "Starting new game at %lf",
|
||||
StkTime::getRealTime());
|
||||
uint64_t start_time = event->data().getUInt64();
|
||||
joinStartGameThread();
|
||||
m_start_game_thread = std::thread([start_time, this]()
|
||||
{
|
||||
const uint64_t cur_time = STKHost::get()->getNetworkTimer();
|
||||
if (!(start_time > cur_time))
|
||||
{
|
||||
Log::warn("ClientLobby", "Network timer is too slow to catch "
|
||||
"up, you must have a poor network.");
|
||||
STKHost::get()->setErrorMessage(
|
||||
m_disconnected_msg.at(PDI_BAD_CONNECTION));
|
||||
STKHost::get()->requestShutdown();
|
||||
return;
|
||||
}
|
||||
int sleep_time = (int)(start_time - cur_time);
|
||||
Log::info("ClientLobby", "Start game after %dms", sleep_time);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(sleep_time));
|
||||
m_state.store(RACING);
|
||||
});
|
||||
} // startGame
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Called from WorldStatus when reaching the READY phase, i.e. when the race
|
||||
* was started. It is going to inform the server of the race start. This
|
||||
* allows the server to wait for all clients to start, so the server will
|
||||
* be running behind the client with the biggest latency, which should
|
||||
* make it likely that at local time T on the server all messages from
|
||||
* all clients at their local time T have arrived.
|
||||
*/
|
||||
void ClientLobby::startingRaceNow()
|
||||
{
|
||||
NetworkString* ns = getNetworkString(2);
|
||||
ns->addUInt8(LE_STARTED_RACE);
|
||||
sendToServer(ns, /*reliable*/true);
|
||||
delete ns;
|
||||
Log::verbose("ClientLobby", "StartingRaceNow at %lf",
|
||||
StkTime::getRealTime());
|
||||
} // startingRaceNow
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/*! \brief Called when the kart selection starts.
|
||||
* \param event : Event providing the information (no additional information
|
||||
|
||||
@@ -45,6 +45,7 @@ LobbyProtocol::~LobbyProtocol()
|
||||
if (RaceEventManager::getInstance())
|
||||
RaceEventManager::getInstance()->stop();
|
||||
delete m_game_setup;
|
||||
joinStartGameThread();
|
||||
} // ~LobbyProtocol
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
@@ -26,6 +26,7 @@ class NetworkPlayerProfile;
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
/*!
|
||||
@@ -51,7 +52,6 @@ public:
|
||||
LE_CLIENT_LOADED_WORLD, // Client finished loading world
|
||||
LE_LOAD_WORLD, // Clients should load world
|
||||
LE_START_RACE, // Server to client to start race
|
||||
LE_STARTED_RACE, // Client to server that it has started race
|
||||
LE_START_SELECTION, // inform client to start selection
|
||||
LE_RACE_FINISHED, // race has finished, display result
|
||||
LE_RACE_FINISHED_ACK, // client went back to lobby
|
||||
@@ -76,14 +76,22 @@ public:
|
||||
};
|
||||
|
||||
protected:
|
||||
std::thread m_start_game_thread;
|
||||
|
||||
static std::weak_ptr<LobbyProtocol> m_lobby;
|
||||
|
||||
/** Stores data about the online game to play. */
|
||||
GameSetup* m_game_setup;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
void configRemoteKart(
|
||||
const std::vector<std::shared_ptr<NetworkPlayerProfile> >& players) const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
void joinStartGameThread()
|
||||
{
|
||||
if (m_start_game_thread.joinable())
|
||||
m_start_game_thread.join();
|
||||
}
|
||||
public:
|
||||
|
||||
/** Creates either a client or server lobby protocol as a singleton. */
|
||||
|
||||
@@ -212,8 +212,6 @@ void ServerLobby::setup()
|
||||
// the server are ready:
|
||||
resetPeersReady();
|
||||
m_peers_votes.clear();
|
||||
m_server_delay = std::numeric_limits<double>::max();
|
||||
m_server_max_ping = std::numeric_limits<double>::max();
|
||||
m_timeout.store(std::numeric_limits<float>::max());
|
||||
m_waiting_for_reset = false;
|
||||
|
||||
@@ -307,7 +305,6 @@ bool ServerLobby::notifyEventAsynchronous(Event* event)
|
||||
case LE_CONNECTION_REQUESTED: connectionRequested(event); break;
|
||||
case LE_KART_SELECTION: kartSelectionRequested(event); break;
|
||||
case LE_CLIENT_LOADED_WORLD: finishedLoadingWorldClient(event); break;
|
||||
case LE_STARTED_RACE: startedRaceOnClient(event); break;
|
||||
case LE_VOTE: playerVote(event); break;
|
||||
case LE_KICK_HOST: kickHost(event); break;
|
||||
case LE_CHANGE_TEAM: changeTeam(event); break;
|
||||
@@ -439,47 +436,11 @@ void ServerLobby::asynchronousUpdate()
|
||||
return;
|
||||
if (!checkPeersReady())
|
||||
return;
|
||||
m_state = WAIT_FOR_RACE_STARTED;
|
||||
// Reset for next state usage
|
||||
resetPeersReady();
|
||||
signalRaceStartToClients();
|
||||
m_server_max_ping = StkTime::getRealTime() +
|
||||
((double)UserConfigParams::m_max_ping / 1000.0);
|
||||
configPeersStartTime();
|
||||
break;
|
||||
}
|
||||
case WAIT_FOR_RACE_STARTED:
|
||||
{
|
||||
const bool ping_timed_out =
|
||||
m_server_max_ping < StkTime::getRealTime();
|
||||
if (checkPeersReady() || ping_timed_out)
|
||||
{
|
||||
for (auto p : m_peers_ready)
|
||||
{
|
||||
auto cur_peer = p.first.lock();
|
||||
if (!cur_peer)
|
||||
continue;
|
||||
if (ping_timed_out && p.second.second > m_server_max_ping)
|
||||
sendBadConnectionMessageToPeer(cur_peer);
|
||||
}
|
||||
m_state = DELAY_SERVER;
|
||||
const double jt =
|
||||
(double)UserConfigParams::m_jitter_tolerance / 1000.0;
|
||||
m_server_delay = StkTime::getRealTime() + jt;
|
||||
Log::verbose("ServerLobby",
|
||||
"Started delay at %lf to %lf with jitter tolerance %lf.",
|
||||
StkTime::getRealTime(), m_server_delay, jt);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DELAY_SERVER:
|
||||
if (m_server_delay < StkTime::getRealTime())
|
||||
{
|
||||
Log::verbose("ServerLobby", "End delay at %lf",
|
||||
StkTime::getRealTime());
|
||||
m_state = RACING;
|
||||
World::getWorld()->setReadyToRace();
|
||||
}
|
||||
break;
|
||||
case SELECTING:
|
||||
{
|
||||
auto result = handleVote();
|
||||
@@ -634,7 +595,6 @@ void ServerLobby::update(int ticks)
|
||||
case ACCEPTING_CLIENTS:
|
||||
case WAIT_FOR_WORLD_LOADED:
|
||||
case WAIT_FOR_RACE_STARTED:
|
||||
case DELAY_SERVER:
|
||||
{
|
||||
// Waiting for asynchronousUpdate
|
||||
break;
|
||||
@@ -779,21 +739,6 @@ void ServerLobby::unregisterServer(bool now)
|
||||
|
||||
} // unregisterServer
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** This function is called when all clients have loaded the world and
|
||||
* are therefore ready to start the race. It signals to all clients
|
||||
* to start the race and then switches state to DELAY_SERVER.
|
||||
*/
|
||||
void ServerLobby::signalRaceStartToClients()
|
||||
{
|
||||
Log::verbose("Server", "Signaling race start to clients at %lf",
|
||||
StkTime::getRealTime());
|
||||
NetworkString *ns = getNetworkString(1);
|
||||
ns->addUInt8(LE_START_RACE);
|
||||
sendMessageToPeers(ns, /*reliable*/true);
|
||||
delete ns;
|
||||
} // startGame
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Instructs all clients to start the kart selection. If event is NULL,
|
||||
* the command comes from the owner less server.
|
||||
@@ -1907,28 +1852,6 @@ void ServerLobby::finishedLoadingWorldClient(Event *event)
|
||||
peer->getHostId(), StkTime::getRealTime());
|
||||
} // finishedLoadingWorldClient
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Called when a notification from a client is received that the client has
|
||||
* started the race. Once all clients have informed the server that they
|
||||
* have started the race, the server can start. This makes sure that the
|
||||
* server's local time is behind all clients by at most max ping,
|
||||
* which in turn means that when the server simulates local time T all
|
||||
* messages from clients at their local time T should have arrived at
|
||||
* the server, which creates smoother play experience.
|
||||
*/
|
||||
void ServerLobby::startedRaceOnClient(Event *event)
|
||||
{
|
||||
if (m_state.load() != WAIT_FOR_RACE_STARTED)
|
||||
{
|
||||
sendBadConnectionMessageToPeer(event->getPeerSP());
|
||||
return;
|
||||
}
|
||||
std::shared_ptr<STKPeer> peer = event->getPeerSP();
|
||||
m_peers_ready.at(peer) = std::make_pair(true, StkTime::getRealTime());
|
||||
Log::info("ServerLobby", "Peer %s has started race at %lf",
|
||||
peer->getAddress().toString().c_str(), StkTime::getRealTime());
|
||||
} // startedRaceOnClient
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Called when a client clicks on 'ok' on the race result screen.
|
||||
* If all players have clicked on 'ok', go back to the lobby.
|
||||
@@ -2114,3 +2037,52 @@ void ServerLobby::submitRankingsToAddons()
|
||||
request->queue();
|
||||
}
|
||||
} // submitRankingsToAddons
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** This function is called when all clients have loaded the world and
|
||||
* are therefore ready to start the race. It determine the start time in
|
||||
* network timer for client and server based on pings and then switches state
|
||||
* to WAIT_FOR_RACE_STARTED.
|
||||
*/
|
||||
void ServerLobby::configPeersStartTime()
|
||||
{
|
||||
uint32_t max_ping = 0;
|
||||
const unsigned max_ping_from_peers = UserConfigParams::m_max_ping;
|
||||
for (auto p : m_peers_ready)
|
||||
{
|
||||
auto peer = p.first.lock();
|
||||
if (!peer)
|
||||
continue;
|
||||
if (peer->getAveragePing() > max_ping_from_peers)
|
||||
{
|
||||
sendBadConnectionMessageToPeer(peer);
|
||||
continue;
|
||||
}
|
||||
max_ping = std::max(peer->getAveragePing(), max_ping);
|
||||
}
|
||||
// Start up time will be after 2000ms, so even if this packet is sent late
|
||||
// (due to packet loss), the start time will still ahead of current time
|
||||
uint64_t start_time = STKHost::get()->getNetworkTimer() + (uint64_t)2000;
|
||||
NetworkString* ns = getNetworkString(10);
|
||||
ns->addUInt8(LE_START_RACE).addUInt64(start_time);
|
||||
sendMessageToPeers(ns, /*reliable*/true);
|
||||
|
||||
const unsigned jitter_tolerance = UserConfigParams::m_jitter_tolerance;
|
||||
Log::info("ServerLobby", "Max ping from peers: %d, jitter tolerance: %d",
|
||||
max_ping, jitter_tolerance);
|
||||
// Delay server for max ping / 2 from peers and jitter tolerance.
|
||||
start_time += (uint64_t)(max_ping / 2) + (uint64_t)jitter_tolerance;
|
||||
delete ns;
|
||||
m_state = WAIT_FOR_RACE_STARTED;
|
||||
|
||||
joinStartGameThread();
|
||||
m_start_game_thread = std::thread([start_time, this]()
|
||||
{
|
||||
const uint64_t cur_time = STKHost::get()->getNetworkTimer();
|
||||
assert(start_time > cur_time);
|
||||
int sleep_time = (int)(start_time - cur_time);
|
||||
Log::info("ServerLobby", "Start game after %dms", sleep_time);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(sleep_time));
|
||||
m_state.store(RACING);
|
||||
});
|
||||
} // configPeersStartTime
|
||||
|
||||
@@ -51,7 +51,6 @@ public:
|
||||
LOAD_WORLD, // Server starts loading world
|
||||
WAIT_FOR_WORLD_LOADED, // Wait for clients and server to load world
|
||||
WAIT_FOR_RACE_STARTED, // Wait for all clients to have started the race
|
||||
DELAY_SERVER, // Additional server delay
|
||||
RACING, // racing
|
||||
WAIT_FOR_RACE_STOPPED, // Wait server for stopping all race protocols
|
||||
RESULT_DISPLAY, // Show result screen
|
||||
@@ -90,12 +89,6 @@ private:
|
||||
std::map<std::weak_ptr<STKPeer>, std::tuple<std::string, uint8_t, bool>,
|
||||
std::owner_less<std::weak_ptr<STKPeer> > > m_peers_votes;
|
||||
|
||||
/** Keeps track of an artificial server delay, which is used to compensate
|
||||
* for network jitter. */
|
||||
double m_server_delay;
|
||||
|
||||
double m_server_max_ping;
|
||||
|
||||
bool m_has_created_server_id_file;
|
||||
|
||||
/** It indicates if this server is unregistered with the stk server. */
|
||||
@@ -153,7 +146,6 @@ private:
|
||||
void playerFinishedResult(Event *event);
|
||||
bool registerServer();
|
||||
void finishedLoadingWorldClient(Event *event);
|
||||
void startedRaceOnClient(Event *event);
|
||||
void kickHost(Event* event);
|
||||
void changeTeam(Event* event);
|
||||
void handleChat(Event* event);
|
||||
@@ -220,6 +212,7 @@ private:
|
||||
void checkRaceFinished();
|
||||
void sendBadConnectionMessageToPeer(std::shared_ptr<STKPeer> p);
|
||||
std::pair<int, float> getHitCaptureLimit(float num_karts);
|
||||
void configPeersStartTime();
|
||||
public:
|
||||
ServerLobby();
|
||||
virtual ~ServerLobby();
|
||||
@@ -230,7 +223,6 @@ public:
|
||||
virtual void update(int ticks) OVERRIDE;
|
||||
virtual void asynchronousUpdate() OVERRIDE;
|
||||
|
||||
void signalRaceStartToClients();
|
||||
void startSelection(const Event *event=NULL);
|
||||
void checkIncomingConnectionRequests();
|
||||
void finishedLoadingWorld() OVERRIDE;
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
#include "utils/separate_process.hpp"
|
||||
#include "utils/time.hpp"
|
||||
#include "utils/vs.hpp"
|
||||
#include "main_loop.hpp"
|
||||
|
||||
#include <string.h>
|
||||
#if defined(WIN32)
|
||||
@@ -304,12 +303,12 @@ STKHost::STKHost(bool server)
|
||||
*/
|
||||
void STKHost::init()
|
||||
{
|
||||
m_network_timer.store(StkTime::getRealTimeMs());
|
||||
m_shutdown = false;
|
||||
m_authorised = false;
|
||||
m_network = NULL;
|
||||
m_exit_timeout.store(std::numeric_limits<double>::max());
|
||||
m_client_ping.store(0);
|
||||
main_loop->resetStartNetworkGameTimer();
|
||||
|
||||
// Start with initialising ENet
|
||||
// ============================
|
||||
@@ -337,7 +336,6 @@ void STKHost::init()
|
||||
*/
|
||||
STKHost::~STKHost()
|
||||
{
|
||||
main_loop->resetStartNetworkGameTimer();
|
||||
requestShutdown();
|
||||
if (m_network_console.joinable())
|
||||
m_network_console.join();
|
||||
@@ -779,7 +777,7 @@ void STKHost::mainLoop()
|
||||
if (need_ping)
|
||||
{
|
||||
BareNetworkString ping_packet;
|
||||
uint64_t network_timer = main_loop->getNetworkTimer();
|
||||
uint64_t network_timer = getNetworkTimer();
|
||||
ping_packet.addUInt64(network_timer);
|
||||
ping_packet.addUInt8((uint8_t)m_peer_pings.getData().size());
|
||||
for (auto& p : m_peer_pings.getData())
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "network/network_string.hpp"
|
||||
#include "network/transport_address.hpp"
|
||||
#include "utils/synchronised.hpp"
|
||||
#include "utils/time.hpp"
|
||||
|
||||
#include "irrString.h"
|
||||
|
||||
@@ -138,6 +139,8 @@ private:
|
||||
|
||||
std::atomic<uint32_t> m_client_ping;
|
||||
|
||||
std::atomic<uint64_t> m_network_timer;
|
||||
|
||||
std::unique_ptr<NetworkTimerSynchronizer> m_nts;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
@@ -318,7 +321,12 @@ public:
|
||||
// ------------------------------------------------------------------------
|
||||
NetworkTimerSynchronizer* getNetworkTimerSynchronizer() const
|
||||
{ return m_nts.get(); }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
uint64_t getNetworkTimer() const
|
||||
{ return StkTime::getRealTimeMs() - m_network_timer.load(); }
|
||||
// ------------------------------------------------------------------------
|
||||
void setNetworkTimer(uint64_t ticks)
|
||||
{ m_network_timer.store(StkTime::getRealTimeMs() - ticks); }
|
||||
}; // class STKHost
|
||||
|
||||
#endif // STK_HOST_HPP
|
||||
|
||||
@@ -39,7 +39,7 @@ STKPeer::STKPeer(ENetPeer *enet_peer, STKHost* host, uint32_t host_id)
|
||||
m_host_id = host_id;
|
||||
m_connected_time = (float)StkTime::getRealTime();
|
||||
m_validated.store(false);
|
||||
m_average_ping = 0;
|
||||
m_average_ping.store(0);
|
||||
} // STKPeer
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -165,9 +165,9 @@ uint32_t STKPeer::getPing()
|
||||
while (m_previous_pings.size() > ap)
|
||||
{
|
||||
m_previous_pings.pop_front();
|
||||
m_average_ping =
|
||||
m_average_ping.store(
|
||||
(uint32_t)(std::accumulate(m_previous_pings.begin(),
|
||||
m_previous_pings.end(), 0) / m_previous_pings.size());
|
||||
m_previous_pings.end(), 0) / m_previous_pings.size()));
|
||||
}
|
||||
}
|
||||
return m_enet_peer->roundTripTime;
|
||||
|
||||
@@ -81,7 +81,7 @@ protected:
|
||||
|
||||
std::deque<uint32_t> m_previous_pings;
|
||||
|
||||
uint32_t m_average_ping;
|
||||
std::atomic<uint32_t> m_average_ping;
|
||||
|
||||
public:
|
||||
STKPeer(ENetPeer *enet_peer, STKHost* host, uint32_t host_id);
|
||||
@@ -165,7 +165,7 @@ public:
|
||||
// ------------------------------------------------------------------------
|
||||
void setCrypto(std::unique_ptr<Crypto>&& c);
|
||||
// ------------------------------------------------------------------------
|
||||
uint32_t getAveragePing() const { return m_average_ping; }
|
||||
uint32_t getAveragePing() const { return m_average_ping.load(); }
|
||||
// ------------------------------------------------------------------------
|
||||
ENetPeer* getENetPeer() const { return m_enet_peer; }
|
||||
}; // STKPeer
|
||||
|
||||
@@ -230,13 +230,13 @@ void RaceGUI::renderGlobal(float dt)
|
||||
|
||||
World *world = World::getWorld();
|
||||
assert(world != NULL);
|
||||
if(world->getPhase() >= WorldStatus::READY_PHASE &&
|
||||
if(world->getPhase() >= WorldStatus::WAIT_FOR_SERVER_PHASE &&
|
||||
world->getPhase() <= WorldStatus::GO_PHASE )
|
||||
{
|
||||
drawGlobalReadySetGo();
|
||||
}
|
||||
if(world->getPhase() == World::GOAL_PHASE)
|
||||
drawGlobalGoal();
|
||||
drawGlobalGoal();
|
||||
|
||||
// MiniMap is drawn when the players wait for the start countdown to end
|
||||
drawGlobalMiniMap();
|
||||
|
||||
@@ -70,6 +70,9 @@ RaceGUIBase::RaceGUIBase()
|
||||
m_string_go = _("Go!");
|
||||
//I18N: Shown when a goal is scored
|
||||
m_string_goal = _("GOAL!");
|
||||
// I18N: Shown waiting for other players in network to finish loading or
|
||||
// waiting
|
||||
m_string_waiting_for_others = _("Waiting for others");
|
||||
|
||||
m_music_icon = irr_driver->getTexture("notes.png");
|
||||
if (!m_music_icon)
|
||||
@@ -626,6 +629,18 @@ void RaceGUIBase::drawGlobalReadySetGo()
|
||||
{
|
||||
switch (World::getWorld()->getPhase())
|
||||
{
|
||||
case WorldStatus::WAIT_FOR_SERVER_PHASE:
|
||||
{
|
||||
static video::SColor color = video::SColor(255, 255, 255, 255);
|
||||
core::rect<s32> pos(irr_driver->getActualScreenSize().Width>>1,
|
||||
irr_driver->getActualScreenSize().Height>>1,
|
||||
irr_driver->getActualScreenSize().Width>>1,
|
||||
irr_driver->getActualScreenSize().Height>>1);
|
||||
gui::IGUIFont* font = GUIEngine::getTitleFont();
|
||||
font->draw(StringUtils::loadingDots(
|
||||
m_string_waiting_for_others.c_str()), pos, color, true, true);
|
||||
}
|
||||
break;
|
||||
case WorldStatus::READY_PHASE:
|
||||
{
|
||||
static video::SColor color = video::SColor(255, 255, 255, 255);
|
||||
|
||||
@@ -133,7 +133,8 @@ private:
|
||||
video::ITexture* m_plunger_face;
|
||||
|
||||
/** Translated strings 'ready', 'set', 'go'. */
|
||||
core::stringw m_string_ready, m_string_set, m_string_go, m_string_goal;
|
||||
core::stringw m_string_ready, m_string_set, m_string_go, m_string_goal,
|
||||
m_string_waiting_for_others;
|
||||
|
||||
/** The position of the referee for all karts. */
|
||||
std::vector<Vec3> m_referee_pos;
|
||||
|
||||
Reference in New Issue
Block a user