diff --git a/src/main_loop.cpp b/src/main_loop.cpp index 29d9a1fad..2db85e3e5 100644 --- a/src/main_loop.cpp +++ b/src/main_loop.cpp @@ -34,6 +34,7 @@ #include "network/protocol_manager.hpp" #include "network/race_event_manager.hpp" #include "network/stk_host.hpp" +#include "network/protocols/synchronization_protocol.hpp" #include "online/request_manager.hpp" #include "race/history.hpp" #include "race/race_manager.hpp" @@ -288,9 +289,18 @@ void MainLoop::run() PROFILER_POP_CPU_MARKER(); } - // Update world time if world exists - if (World::getWorld()) - World::getWorld()->updateTime(dt); + if (World::getWorld() ) + { + // In case of networking world we can only start the timing once the + // SynchronizationProtocol has disappeared (which indicates that all + // other protocols necessary for running a game are running). + SynchronizationProtocol* protocol = static_cast( + ProtocolManager::getInstance()->getProtocol(PROTOCOL_SYNCHRONIZATION)); + if (!protocol) + { + World::getWorld()->updateTime(dt); + } + } PROFILER_POP_CPU_MARKER(); PROFILER_SYNC_FRAME(); diff --git a/src/network/protocols/start_game_protocol.cpp b/src/network/protocols/start_game_protocol.cpp index e913b1d10..b5aea6a29 100644 --- a/src/network/protocols/start_game_protocol.cpp +++ b/src/network/protocols/start_game_protocol.cpp @@ -59,7 +59,7 @@ void StartGameProtocol::setup() // This creates the network world. RaceEventManager::getInstance()->start(); - // The number of karts includes the AI karts, which are not supported atn + // The number of karts includes the AI karts, which are not supported atm race_manager->setNumKarts(m_game_setup->getPlayerCount()); // Set number of global and local players. @@ -121,6 +121,11 @@ void StartGameProtocol::setup() } // setup // ---------------------------------------------------------------------------- +/** Handles incoming messages on the server. Should never be called on a + * client. It counts how many clients are ready, and once all clients + * are ready, will call startRace(). + * \param event Details about the received mnessage. + */ bool StartGameProtocol::notifyEventAsynchronous(Event* event) { if(!checkDataSize(event, 1)) return true; @@ -157,7 +162,7 @@ void StartGameProtocol::startRace() static_cast(p); if (protocol) { - protocol->startCountdown(5000); // 5 seconds countdown + protocol->startCountdown(5.0f); // 5 seconds countdown Log::info("StartGameProtocol", "All players ready, starting countdown."); m_ready = true; diff --git a/src/network/protocols/synchronization_protocol.cpp b/src/network/protocols/synchronization_protocol.cpp index c553b79ad..c34334239 100644 --- a/src/network/protocols/synchronization_protocol.cpp +++ b/src/network/protocols/synchronization_protocol.cpp @@ -37,7 +37,12 @@ void SynchronizationProtocol::setup() m_has_quit = false; } // setup //----------------------------------------------------------------------------- - +/** Called when receiving a message. On the client side the message is a ping + * from the server, which is answered back. The client will also check if the + * server has started the countdown (which is indicated in the ping message). + * On the server the received message is a reply to a previous ping request. + * The server will keep track of average latency. + */ bool SynchronizationProtocol::notifyEventAsynchronous(Event* event) { if (event->getType() != EVENT_TYPE_MESSAGE) @@ -79,15 +84,15 @@ bool SynchronizationProtocol::notifyEventAsynchronous(Event* event) // countdown time in the message if (data.size() == 4) { - uint32_t time_to_start = data.getUInt32(); + float time_to_start = data.getFloat(); Log::debug("SynchronizationProtocol", - "Request to start game in %d.", time_to_start); + "Request to start game in %f.", time_to_start); if (!m_countdown_activated) startCountdown(time_to_start); else { // Adjust the time based on the value sent from the server. - m_countdown = (double)(time_to_start/1000.0); + m_countdown = time_to_start; } } else @@ -120,10 +125,19 @@ bool SynchronizationProtocol::notifyEventAsynchronous(Event* event) } // notifyEventAsynchronous //----------------------------------------------------------------------------- - +/** Waits for the countdown to be started. On the server the start of the + * countdown is triggered by the StartGameProtocol::startRace(), which is + * called once all clients have confirmed that they are ready to start. + * The server will send a ping request to each client once a second, and + * include the information if the countdown has started (and its current + * value). On the client the countdown is started in notifyEvenAsynchronous() + * when a server ping is received that indicates that the countdown has + * started. The measured times can be used later to estimate the latency + * between server and client. + */ void SynchronizationProtocol::asynchronousUpdate() { - double current_time = StkTime::getRealTime(); + float current_time = float(StkTime::getRealTime()); if (m_countdown_activated) { m_countdown -= (current_time - m_last_countdown_update); @@ -167,7 +181,7 @@ void SynchronizationProtocol::asynchronousUpdate() // message is received), or to update the countdown time. if (m_countdown_activated) { - ping_request->addUInt32((int)(m_countdown*1000.0)); + ping_request->addFloat(m_countdown); Log::debug("SynchronizationProtocol", "CNTActivated: Countdown value : %f", m_countdown); } @@ -191,11 +205,11 @@ void SynchronizationProtocol::asynchronousUpdate() * the countdown has to be started. * \param ms_countdown Countdown to use in ms. */ -void SynchronizationProtocol::startCountdown(int ms_countdown) +void SynchronizationProtocol::startCountdown(float ms_countdown) { - m_countdown_activated = true; - m_countdown = (double)(ms_countdown)/1000.0; - m_last_countdown_update = StkTime::getRealTime(); + m_countdown_activated = true; + m_countdown = ms_countdown; + m_last_countdown_update = float(StkTime::getRealTime()); Log::info("SynchronizationProtocol", "Countdown started with value %f", m_countdown); } // startCountdown diff --git a/src/network/protocols/synchronization_protocol.hpp b/src/network/protocols/synchronization_protocol.hpp index 4190f2b95..ed586fa11 100644 --- a/src/network/protocols/synchronization_protocol.hpp +++ b/src/network/protocols/synchronization_protocol.hpp @@ -17,9 +17,14 @@ private: uint32_t m_pings_count; std::vector m_successed_pings; std::vector m_total_diff; + + /** True if the countdown has started, i.e. all clients have loaded + * the track and karts. */ bool m_countdown_activated; - double m_countdown; - double m_last_countdown_update; + + /** The countdown timer value. */ + float m_countdown; + float m_last_countdown_update; bool m_has_quit; /** Keeps track of last time that an update was sent. */ @@ -33,12 +38,13 @@ public: virtual bool notifyEventAsynchronous(Event* event) OVERRIDE; virtual void setup() OVERRIDE; virtual void asynchronousUpdate() OVERRIDE; - void startCountdown(int ms_countdown); + void startCountdown(float ms_countdown); // ------------------------------------------------------------------------ virtual void update(float dt) OVERRIDE {} // ------------------------------------------------------------------------ - int getCountdown() { return (int)(m_countdown*1000.0); } + /** Returns the current countdown value. */ + float getCountdown() { return m_countdown; } }; // class SynchronizationProtocol diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index 9f4ec911e..1b0f0842a 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -218,7 +218,22 @@ void STKHost::create() * the 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). + * remote players). It will also start the SynchronizationProtocol. + * The StartGameProtocol has a callback ready which is called from world + * when the world is loaded (i.e. track and all karts are ready). When + * this callback is invoked, each client will send a 'ready' message to + * the server's StartGameProtocol. Once the server has received all + * messages in notifyEventAsynchronous(), it will call startCountdown() + * in the SynchronizationProtocol. The SynchronizationProtocol 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 wellk. + * + * Once the countdown is 0 (or below), the Synchronization Protocol will + * start the protocols: KartUpdateProtocol, ControllerEventsProtocol, + * GameEventsProtocol. Then the SynchronizationProtocol is terminated + * which indicates to the main loop to start the actual game. */ // ============================================================================