Don't use the SynchronisationProtocol for startup state changes at all.

It is now totally optional, and only estimates the latency between the
server and all clients (and this value is not even used).
This commit is contained in:
hiker 2016-11-25 22:17:24 +11:00
parent b1afac23a7
commit 6053ad207f
12 changed files with 155 additions and 191 deletions

View File

@ -28,6 +28,7 @@
#include "karts/abstract_kart.hpp" #include "karts/abstract_kart.hpp"
#include "modes/world.hpp" #include "modes/world.hpp"
#include "network/network_config.hpp" #include "network/network_config.hpp"
#include "network/protocols/client_lobby.hpp"
#include "network/protocols/server_lobby.hpp" #include "network/protocols/server_lobby.hpp"
#include "network/race_event_manager.hpp" #include "network/race_event_manager.hpp"
#include "tracks/track.hpp" #include "tracks/track.hpp"
@ -52,7 +53,6 @@ WorldStatus::WorldStatus()
if (device->getTimer()->isStopped()) if (device->getTimer()->isStopped())
device->getTimer()->start(); device->getTimer()->start();
m_ready_to_race = false;
} // WorldStatus } // WorldStatus
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -94,9 +94,9 @@ void WorldStatus::reset()
// Set the right music // Set the right music
World::getWorld()->getTrack()->startMusic(); World::getWorld()->getTrack()->startMusic();
// In case of a networked race the race can only start once // In case of a networked race the race can only start once
// all protocols are up. This flag waits for that, and is // all protocols are up. This flag is used to wait for
// set by // a confirmation before starting the actual race.
m_ready_to_race = !NetworkConfig::get()->isNetworking(); m_server_is_ready = false;
} // reset } // reset
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -180,10 +180,6 @@ void WorldStatus::update(float dt)
*/ */
void WorldStatus::updateTime(const float dt) void WorldStatus::updateTime(const float dt)
{ {
// In case of a networked race wait till all necessary protocols are
// ready before progressing the timer
// if (!m_ready_to_race) return;
switch (m_phase) switch (m_phase)
{ {
// Note: setup phase must be a separate phase, since the race_manager // Note: setup phase must be a separate phase, since the race_manager
@ -244,47 +240,31 @@ void WorldStatus::updateTime(const float dt)
m_prestart_sound->play(); m_prestart_sound->play();
// In a networked game the client needs to wait for a notification // In a networked game the client needs to wait for a notification
// from the server that ready-set-go can start now. So client will go // from the server that all clients and the server are ready to
// to the 'wait_for_server_phase', from which they will progress once // start the game. The server will actually wait for all clients
// the notification is received. In all other cases (no networking or // to confirm that they have started the race before starting
// server), immediately go to race start // itself. In a normal race, this phase is skipped and the race
if (NetworkConfig::get()->isNetworking()) // starts immediately.
{ m_phase = NetworkConfig::get()->isNetworking() ? WAIT_FOR_SERVER_PHASE
m_phase = WAIT_FOR_SERVER_PHASE; : READY_PHASE;
}
else
{
m_phase = READY_PHASE;
startEngines();
}
return; // Don't increase time return; // Don't increase time
case WAIT_FOR_SERVER_PHASE: case WAIT_FOR_SERVER_PHASE:
// On a client this phase waits for the server to be ready. On a {
// server or in case of non-networked game this phase is reached // This stage is only reached in case of a networked game.
// with m_server_is_ready set to true, so it will immediately // A client waits for a message from the server that it can
// start the engines and then the race // start the race (i.e. that all clients and the server have
if(!m_server_is_ready) return; // loaded the world). 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_server_is_ready) return;
if (NetworkConfig::get()->isNetworking() && m_phase = READY_PHASE;
NetworkConfig::get()->isClient()) Protocol *p = LobbyProtocol::get();
{ ClientLobby *cl = dynamic_cast<ClientLobby*>(p);
// Each client informs the server when its starting the race. if (cl)
// The server waits for the last message (i.e. from the slowest cl->startingRaceNow();
// client) before starting, which means the server's local time
// will always be behind any client's time by the latency to
// that client. This in turn should guarantee that any message
// from the clients reach the server before the server actually
// needs it. By running the server behind the clients the need
// for rollbacks on the server is greatly reduced.
//RaceEventManager::getInstance()->clientHasStarted();
}
m_phase = READY_PHASE;
// Receiving a 'startReadySetGo' message from the server triggers
// a call to startReadySetGo() here, which will change the phase
// (or state) of the finite state machine.
return; // Don't increase time return; // Don't increase time
}
case READY_PHASE: case READY_PHASE:
startEngines(); startEngines();

View File

@ -94,9 +94,6 @@ protected:
/** If the start race should be played, disabled in cutscenes. */ /** If the start race should be played, disabled in cutscenes. */
bool m_play_racestart_sounds; bool m_play_racestart_sounds;
/** A flag that causes the world to wait in case of a networking race
* till all protocols are up and running. */
bool m_ready_to_race;
private: private:
/** Sound to play at the beginning of a race, during which a /** Sound to play at the beginning of a race, during which a
* a camera intro of the track can be shown. */ * a camera intro of the track can be shown. */
@ -130,10 +127,10 @@ private:
bool m_engines_started; bool m_engines_started;
void startEngines(); void startEngines();
/** In networked game the client must wait for the server to start 'ready set go' /** In networked game a client must wait for the server to start 'ready
* (to guarantee that the client's time is not ahead of the server), This flag * set go' to make sure all client are actually ready to start the game.
* indicates that the notification from the server was received, and that the * A server on the other hand will run behind all clients, so it will
* client can go to 'ready' phase. */ * wait for all clients to indicate that they have started the race. */
bool m_server_is_ready; bool m_server_is_ready;
public: public:

View File

@ -24,6 +24,7 @@
#include "network/network_config.hpp" #include "network/network_config.hpp"
#include "network/network_player_profile.hpp" #include "network/network_player_profile.hpp"
#include "network/protocol_manager.hpp" #include "network/protocol_manager.hpp"
#include "network/protocols/synchronization_protocol.hpp"
#include "network/race_event_manager.hpp" #include "network/race_event_manager.hpp"
#include "network/stk_host.hpp" #include "network/stk_host.hpp"
#include "network/stk_peer.hpp" #include "network/stk_peer.hpp"
@ -316,6 +317,10 @@ void ClientLobby::update(float dt)
NetworkKartSelectionScreen::getInstance(); NetworkKartSelectionScreen::getInstance();
screen->push(); screen->push();
m_state = SELECTING_KARTS; m_state = SELECTING_KARTS;
Protocol *p = new SynchronizationProtocol();
p->requestStart();
Log::info("LobbyProtocol", "SynchronizationProtocol started.");
} }
break; break;
case SELECTING_KARTS: case SELECTING_KARTS:
@ -585,7 +590,7 @@ void ClientLobby::kartSelectionUpdate(Event* event)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
/*! \brief Called when the server broadcasts the race start. /*! \brief Called when the server broadcasts to start the race.
race needs to be started. race needs to be started.
* \param event : Event providing the information (no additional information * \param event : Event providing the information (no additional information
* in this case). * in this case).
@ -593,12 +598,29 @@ race needs to be started.
void ClientLobby::startGame(Event* event) void ClientLobby::startGame(Event* event)
{ {
m_state = PLAYING; m_state = PLAYING;
// Triggers the world finite state machine to go from WAIT_FOR_SERVER_PHASE
// to READY_PHASE.
World::getWorld()->setReadyToRace(); World::getWorld()->setReadyToRace();
Log::info("ClientLobby", "Starting new game"); Log::info("ClientLobby", "Starting new game");
} // startGame } // 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);
terminateSynchronizationProtocol();
} // startingRaceNow
//-----------------------------------------------------------------------------
/*! \brief Called when the kart selection starts. /*! \brief Called when the kart selection starts.
* \param event : Event providing the information (no additional information * \param event : Event providing the information (no additional information
* in this case). * in this case).

View File

@ -64,6 +64,7 @@ public:
void voteReversed(uint8_t player_id, bool reversed, uint8_t track_nb = 0); void voteReversed(uint8_t player_id, bool reversed, uint8_t track_nb = 0);
void voteLaps(uint8_t player_id, uint8_t laps, uint8_t track_nb = 0); void voteLaps(uint8_t player_id, uint8_t laps, uint8_t track_nb = 0);
void doneWithResults(); void doneWithResults();
void startingRaceNow();
void leave(); void leave();
virtual bool notifyEvent(Event* event) OVERRIDE; virtual bool notifyEvent(Event* event) OVERRIDE;

View File

@ -40,7 +40,6 @@ GameEventsProtocol::~GameEventsProtocol()
void GameEventsProtocol::setup() void GameEventsProtocol::setup()
{ {
m_count_ready_clients = 0; m_count_ready_clients = 0;
World::getWorld()->setReadyToRace();
} // setup } // setup
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View File

@ -23,6 +23,7 @@
#include "input/device_manager.hpp" #include "input/device_manager.hpp"
#include "modes/world.hpp" #include "modes/world.hpp"
#include "network/network_player_profile.hpp" #include "network/network_player_profile.hpp"
#include "network/protocol_manager.hpp"
#include "network/protocols/controller_events_protocol.hpp" #include "network/protocols/controller_events_protocol.hpp"
#include "network/protocols/game_events_protocol.hpp" #include "network/protocols/game_events_protocol.hpp"
#include "network/protocols/kart_update_protocol.hpp" #include "network/protocols/kart_update_protocol.hpp"
@ -57,10 +58,6 @@ void LobbyProtocol::loadWorld()
{ {
Log::info("LobbyProtocol", "Ready !"); Log::info("LobbyProtocol", "Ready !");
Protocol *p = new SynchronizationProtocol();
p->requestStart();
Log::info("LobbyProtocol", "SynchronizationProtocol started.");
// Race startup sequence // Race startup sequence
// --------------------- // ---------------------
// This creates the network world. // This creates the network world.
@ -133,3 +130,14 @@ void LobbyProtocol::loadWorld()
} // loadWorld } // loadWorld
// ----------------------------------------------------------------------------
/** Terminates the SynchronizationProtocol.
*/
void LobbyProtocol::terminateSynchronizationProtocol()
{
Protocol *p = ProtocolManager::getInstance()
->getProtocol(PROTOCOL_SYNCHRONIZATION);
SynchronizationProtocol *sp = dynamic_cast<SynchronizationProtocol*>(p);
if (sp)
sp->requestTerminate();
} // stopSynchronizationProtocol

View File

@ -47,7 +47,8 @@ public:
LE_PLAYER_DISCONNECTED, // Client disconnected LE_PLAYER_DISCONNECTED, // Client disconnected
LE_CLIENT_LOADED_WORLD, // Client finished loading world LE_CLIENT_LOADED_WORLD, // Client finished loading world
LE_LOAD_WORLD, // Clients should load world LE_LOAD_WORLD, // Clients should load world
LE_START_RACE, // start race 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_START_SELECTION, // inform client to start selection
LE_RACE_FINISHED, // race has finished, display result LE_RACE_FINISHED, // race has finished, display result
LE_RACE_FINISHED_ACK, // client went back to lobby LE_RACE_FINISHED_ACK, // client went back to lobby
@ -94,6 +95,7 @@ public:
virtual void update(float dt) = 0; virtual void update(float dt) = 0;
virtual void finishedLoadingWorld() = 0; virtual void finishedLoadingWorld() = 0;
virtual void loadWorld(); virtual void loadWorld();
void terminateSynchronizationProtocol();
virtual void requestKartSelection(uint8_t player_id, virtual void requestKartSelection(uint8_t player_id,
const std::string &kart_name) const std::string &kart_name)
{ {

View File

@ -58,12 +58,15 @@
"SELECTING" -> "playerVoteTrack" [label="Client selected track"] "SELECTING" -> "playerVoteTrack" [label="Client selected track"]
"playerVoteTrack" -> "SELECTING" [label="Not all clients have selected"] "playerVoteTrack" -> "SELECTING" [label="Not all clients have selected"]
"playerVoteTrack" -> "LOAD_WORLD" [label="All clients have selected"] "playerVoteTrack" -> "LOAD_WORLD" [label="All clients have selected"]
"LOAD"WORLD -> "WAIT_FOR_WORLD_LOADED" "LOAD_WORLD" -> "WAIT_FOR_WORLD_LOADED"
"WAIT_FOR_WORLD_LOADED" -> "WAIT_FOR_WORLD_LOADED" [label="Client or server loaded world"] "WAIT_FOR_WORLD_LOADED" -> "WAIT_FOR_WORLD_LOADED" [label="Client or server loaded world"]
"WAIT_FOR_WORLD_LOADED" -> "startRace" [label="All clients and server ready"] "WAIT_FOR_WORLD_LOADED" -> "signalRaceStartToClients" [label="All clients and server ready"]
"startRace" -> "DELAY_SERVER" "signalRaceStartToClients" -> "WAIT_FOR_RACE_STARTED"
"WAIT_FOR_RACE_STARTED" -> "startedRaceOnClient"
"startedRaceOnClient" -> "WAIT_FOR_RACE_STARTED" [label="Not all clients have started"]
"startedRaceOnClient" -> "DELAY_SERVER" [label="All clients have started"]
"DELAY_SERVER" -> "DELAY_SERVER" [label="Not done waiting"] "DELAY_SERVER" -> "DELAY_SERVER" [label="Not done waiting"]
"DELAY_SERVER -> "Start Countdown" [label="Server starts race now"] "DELAY_SERVER" -> "Start Countdown" [label="Server starts race now"]
"Start Countdown" -> "Wait For Clients" "Start Countdown" -> "Wait For Clients"
"Wait For Clients" -> "Wait For Clients" [label="Client started 'ready set go'"] "Wait For Clients" -> "Wait For Clients" [label="Client started 'ready set go'"]
"Wait For Clients" -> "Additional Server Delay" "Wait For Clients" -> "Additional Server Delay"
@ -107,7 +110,6 @@ void ServerLobby::setup()
// the server are ready: // the server are ready:
m_player_states.clear(); m_player_states.clear();
m_client_ready_count.setAtomic(0); m_client_ready_count.setAtomic(0);
m_server_delay = 0.02f;
m_server_has_loaded_world = false; m_server_has_loaded_world = false;
const std::vector<NetworkPlayerProfile*> &players = const std::vector<NetworkPlayerProfile*> &players =
m_game_setup->getPlayers(); m_game_setup->getPlayers();
@ -137,6 +139,7 @@ bool ServerLobby::notifyEventAsynchronous(Event* event)
case LE_REQUEST_BEGIN: startSelection(event); break; case LE_REQUEST_BEGIN: startSelection(event); break;
case LE_KART_SELECTION: kartSelectionRequested(event); break; case LE_KART_SELECTION: kartSelectionRequested(event); break;
case LE_CLIENT_LOADED_WORLD: finishedLoadingWorldClient(event); break; case LE_CLIENT_LOADED_WORLD: finishedLoadingWorldClient(event); break;
case LE_STARTED_RACE: startedRaceOnClient(event); break;
case LE_VOTE_MAJOR: playerMajorVote(event); break; case LE_VOTE_MAJOR: playerMajorVote(event); break;
case LE_VOTE_RACE_COUNT: playerRaceCountVote(event); break; case LE_VOTE_RACE_COUNT: playerRaceCountVote(event); break;
case LE_VOTE_MINOR: playerMinorVote(event); break; case LE_VOTE_MINOR: playerMinorVote(event); break;
@ -172,19 +175,19 @@ void ServerLobby::update(float dt)
requestPause(); requestPause();
break; break;
case GETTING_PUBLIC_ADDRESS: case GETTING_PUBLIC_ADDRESS:
{ {
Log::debug("ServerLobby", "Public address known."); Log::debug("ServerLobby", "Public address known.");
// Free GetPublicAddress protocol // Free GetPublicAddress protocol
delete m_current_protocol; delete m_current_protocol;
// Register this server with the STK server. This will block // Register this server with the STK server. This will block
// this thread, but there is no need for the protocol manager // this thread, but there is no need for the protocol manager
// to react to any requests before the server is registered. // to react to any requests before the server is registered.
registerServer(); registerServer();
Log::info("ServerLobby", "Server registered."); Log::info("ServerLobby", "Server registered.");
m_state = ACCEPTING_CLIENTS; m_state = ACCEPTING_CLIENTS;
} }
break; break;
case ACCEPTING_CLIENTS: case ACCEPTING_CLIENTS:
{ {
// Only poll the STK server if this is a WAN server. // Only poll the STK server if this is a WAN server.
@ -194,7 +197,7 @@ void ServerLobby::update(float dt)
} }
case SELECTING: case SELECTING:
// The function playerVoteTrack will trigger the next state // The function playerVoteTrack will trigger the next state
// one all track votes have been received. // once all track votes have been received.
break; break;
case LOAD_WORLD: case LOAD_WORLD:
Log::info("ServerLobbyRoom", "Starting the race loading."); Log::info("ServerLobbyRoom", "Starting the race loading.");
@ -211,14 +214,22 @@ void ServerLobby::update(float dt)
{ {
signalRaceStartToClients(); signalRaceStartToClients();
} }
// Initialise counter again, to wait for all clients to indicate that
// they have started the race/
m_server_delay = 0.02f;
m_client_ready_count.getData() = 0;
m_client_ready_count.unlock(); m_client_ready_count.unlock();
break; break;
case WAIT_FOR_RACE_STARTED:
// The function finishedLoadingWorldClient() will trigger the
// next state.
break;
case DELAY_SERVER: case DELAY_SERVER:
m_server_delay -= dt; m_server_delay -= dt;
if (m_server_delay < 0) if (m_server_delay < 0)
{ {
m_state = RACING; m_state = RACING;
World::getWorld()->setReadyToRace();
} }
break; break;
case RACING: case RACING:
@ -319,7 +330,7 @@ void ServerLobby::signalRaceStartToClients()
ns->addUInt8(LE_START_RACE); ns->addUInt8(LE_START_RACE);
sendMessageToPeersChangingToken(ns, /*reliable*/true); sendMessageToPeersChangingToken(ns, /*reliable*/true);
delete ns; delete ns;
m_state = DELAY_SERVER; m_state = WAIT_FOR_RACE_STARTED;
} // startGame } // startGame
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -328,6 +339,13 @@ void ServerLobby::signalRaceStartToClients()
*/ */
void ServerLobby::startSelection(const Event *event) void ServerLobby::startSelection(const Event *event)
{ {
if (m_state != ACCEPTING_CLIENTS)
{
Log::warn("ServerLobby",
"Received startSelection while being in state %d", m_state);
return;
}
if(event && !event->getPeer()->isAuthorised()) if(event && !event->getPeer()->isAuthorised())
{ {
Log::warn("ServerLobby", Log::warn("ServerLobby",
@ -346,6 +364,11 @@ void ServerLobby::startSelection(const Event *event)
m_state = SELECTING; m_state = SELECTING;
WaitingForOthersScreen::getInstance()->push(); WaitingForOthersScreen::getInstance()->push();
Protocol *p = new SynchronizationProtocol();
p->requestStart();
Log::info("LobbyProtocol", "SynchronizationProtocol started.");
} // startSelection } // startSelection
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -862,28 +885,39 @@ void ServerLobby::finishedLoadingWorldClient(Event *event)
m_client_ready_count.unlock(); m_client_ready_count.unlock();
return; return;
} }
Log::info("ServerLobbyeProtocol", "One of the players is ready."); Log::info("ServerLobbyeProtocol", "Player %d is ready (%d/%d).",
player_id, m_client_ready_count.getData(),
m_game_setup->getPlayerCount() );
m_player_states[player_id] = true; m_player_states[player_id] = true;
m_client_ready_count.getData()++; m_client_ready_count.getData()++;
} }
m_client_ready_count.unlock(); m_client_ready_count.unlock();
if (m_client_ready_count.getAtomic() == m_game_setup->getPlayerCount())
{
Protocol *p = ProtocolManager::getInstance()
->getProtocol(PROTOCOL_SYNCHRONIZATION);
SynchronizationProtocol* protocol =
dynamic_cast<SynchronizationProtocol*>(p);
if (protocol)
{
protocol->startCountdown(5.0f); // 5 seconds countdown
Log::info("ServerLobbyProtocol",
"All players ready, starting countdown.");
}
} // if m_ready_count == number of players
} // finishedLoadingWorldClient } // 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 least) their latency,
* 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)
{
m_client_ready_count.lock();
Log::verbose("ServerLobby", "Host %d has started race.",
event->getPeer()->getHostId());
m_client_ready_count.getData()++;
if (m_client_ready_count.getData() == m_game_setup->getPlayerCount())
{
m_state = DELAY_SERVER;
terminateSynchronizationProtocol();
}
m_client_ready_count.unlock();
} // startedRaceOnClient
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
/** Called when a client clicks on 'ok' on the race result screen. /** Called when a client clicks on 'ok' on the race result screen.
* If all players have clicked on 'ok', go back to the lobby. * If all players have clicked on 'ok', go back to the lobby.

View File

@ -18,6 +18,7 @@ private:
SELECTING, // kart, track, ... selection started SELECTING, // kart, track, ... selection started
LOAD_WORLD, // Server starts loading world LOAD_WORLD, // Server starts loading world
WAIT_FOR_WORLD_LOADED, // Wait for clients and server to load 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
START_RACE, // Inform clients to start race START_RACE, // Inform clients to start race
DELAY_SERVER, // Additional server delay DELAY_SERVER, // Additional server delay
RACING, // racing RACING, // racing
@ -69,6 +70,7 @@ private:
void playerFinishedResult(Event *event); void playerFinishedResult(Event *event);
void registerServer(); void registerServer();
void finishedLoadingWorldClient(Event *event); void finishedLoadingWorldClient(Event *event);
void startedRaceOnClient(Event *event);
public: public:
ServerLobby(); ServerLobby();
virtual ~ServerLobby(); virtual ~ServerLobby();

View File

@ -7,7 +7,16 @@
#include "utils/time.hpp" #include "utils/time.hpp"
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
/** This protocol tries to determine the average latency between client and
* server. While this information is not used atm, it might be useful for
* the server to determine how much behind the clients it should start.
* FIXME: ATM the main thread will load the world as part of an update
* of the ProtocolManager (which updates the protocols). Since all protocols
* are locked dusing this update, the synchronisation protocol is actually
* delayed from starting while world is loading (since finding the protocol
* for a message requires the protocol lock) - causing at least two frames
* of significanlty delayed pings :(
*/
SynchronizationProtocol::SynchronizationProtocol() SynchronizationProtocol::SynchronizationProtocol()
: Protocol(PROTOCOL_SYNCHRONIZATION) : Protocol(PROTOCOL_SYNCHRONIZATION)
{ {
@ -17,8 +26,6 @@ SynchronizationProtocol::SynchronizationProtocol()
m_total_diff.resize(size, 0); m_total_diff.resize(size, 0);
m_average_ping.resize(size, 0); m_average_ping.resize(size, 0);
m_pings_count = 0; m_pings_count = 0;
m_countdown_activated = false;
m_last_time = -1;
} // SynchronizationProtocol } // SynchronizationProtocol
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -30,15 +37,13 @@ SynchronizationProtocol::~SynchronizationProtocol()
void SynchronizationProtocol::setup() void SynchronizationProtocol::setup()
{ {
Log::info("SynchronizationProtocol", "Ready !"); Log::info("SynchronizationProtocol", "Ready !");
m_countdown = 5.0; // init the countdown to 5s
m_has_quit = false;
} // setup } // setup
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
/** Called when receiving a message. On the client side the message is a ping /** 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 * from the server, which is answered back. On the server the received message
* server has started the countdown (which is indicated in the ping message). * is a reply to a previous ping request. The server will keep track of
* On the server the received message is a reply to a previous ping request. * average latency.
* The server will keep track of average latency.
*/ */
bool SynchronizationProtocol::notifyEventAsynchronous(Event* event) bool SynchronizationProtocol::notifyEventAsynchronous(Event* event)
{ {
@ -77,23 +82,6 @@ bool SynchronizationProtocol::notifyEventAsynchronous(Event* event)
delete response; delete response;
Log::verbose("SynchronizationProtocol", "Answering sequence %u at %lf", Log::verbose("SynchronizationProtocol", "Answering sequence %u at %lf",
sequence, StkTime::getRealTime()); sequence, StkTime::getRealTime());
// countdown time in the message
if (data.size() == 4)
{
float time_to_start = data.getFloat();
Log::debug("SynchronizationProtocol",
"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 = time_to_start;
}
}
else
Log::verbose("SynchronizationProtocol", "No countdown for now.");
} }
else // receive response to a ping request else // receive response to a ping request
{ {
@ -135,39 +123,14 @@ bool SynchronizationProtocol::notifyEventAsynchronous(Event* event)
void SynchronizationProtocol::asynchronousUpdate() void SynchronizationProtocol::asynchronousUpdate()
{ {
float current_time = float(StkTime::getRealTime()); float current_time = float(StkTime::getRealTime());
if (m_countdown_activated)
{
m_countdown -= (current_time - m_last_countdown_update);
m_last_countdown_update = current_time;
Log::debug("SynchronizationProtocol",
"Update! Countdown remaining : %f", m_countdown);
if (m_countdown < 0.0 && !m_has_quit)
{
m_has_quit = true;
Log::info("SynchronizationProtocol",
"Countdown finished. Starting now.");
requestTerminate();
return;
}
} // if m_countdown_activated
if (NetworkConfig::get()->isServer() && current_time > m_last_time+1) if (NetworkConfig::get()->isServer() && current_time > m_last_time+1)
{ {
const std::vector<STKPeer*> &peers = STKHost::get()->getPeers(); const std::vector<STKPeer*> &peers = STKHost::get()->getPeers();
for (unsigned int i = 0; i < peers.size(); i++) for (unsigned int i = 0; i < peers.size(); i++)
{ {
NetworkString *ping_request = NetworkString *ping_request =
getNetworkString(m_countdown_activated ? 9 : 5); getNetworkString(5);
ping_request->addUInt8(1).addUInt32(m_pings[i].size()); ping_request->addUInt8(1).addUInt32(m_pings[i].size());
// Server adds the countdown if it has started. This will indicate
// to the client to start the countdown as well (first time the
// message is received), or to update the countdown time.
if (m_countdown_activated)
{
ping_request->addFloat(m_countdown);
Log::debug("SynchronizationProtocol",
"CNTActivated: Countdown value : %f", m_countdown);
}
Log::verbose("SynchronizationProtocol", Log::verbose("SynchronizationProtocol",
"Added sequence number %u for peer %d at %lf", "Added sequence number %u for peer %d at %lf",
m_pings[i].size(), i, StkTime::getRealTime()); m_pings[i].size(), i, StkTime::getRealTime());
@ -180,19 +143,3 @@ void SynchronizationProtocol::asynchronousUpdate()
} // if current_time > m_last_time + 0.1 } // if current_time > m_last_time + 0.1
} // asynchronousUpdate } // asynchronousUpdate
//-----------------------------------------------------------------------------
/** Starts the countdown on this machine. On the server side this function
* is called from ServerLobby::finishedLoadingWorld() (when all
* players have confirmed that they are ready to play). On the client side
* this function is called from this protocol when a message from the server
* is received indicating that the countdown has to be started.
* \param ms_countdown Countdown to use in ms.
*/
void SynchronizationProtocol::startCountdown(float ms_countdown)
{
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

View File

@ -18,15 +18,6 @@ private:
std::vector<uint32_t> m_successed_pings; std::vector<uint32_t> m_successed_pings;
std::vector<double> m_total_diff; std::vector<double> m_total_diff;
/** True if the countdown has started, i.e. all clients have loaded
* the track and karts. */
bool m_countdown_activated;
/** 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. */ /** Keeps track of last time that an update was sent. */
double m_last_time; double m_last_time;
@ -38,13 +29,9 @@ public:
virtual bool notifyEventAsynchronous(Event* event) OVERRIDE; virtual bool notifyEventAsynchronous(Event* event) OVERRIDE;
virtual void setup() OVERRIDE; virtual void setup() OVERRIDE;
virtual void asynchronousUpdate() OVERRIDE; virtual void asynchronousUpdate() OVERRIDE;
void startCountdown(float ms_countdown);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
virtual void update(float dt) OVERRIDE {} virtual void update(float dt) OVERRIDE {}
// ------------------------------------------------------------------------
/** Returns the current countdown value. */
float getCountdown() { return m_countdown; }
}; // class SynchronizationProtocol }; // class SynchronizationProtocol

View File

@ -5,7 +5,6 @@
#include "modes/world.hpp" #include "modes/world.hpp"
#include "network/network_config.hpp" #include "network/network_config.hpp"
#include "network/protocol_manager.hpp" #include "network/protocol_manager.hpp"
#include "network/protocols/synchronization_protocol.hpp"
#include "network/protocols/controller_events_protocol.hpp" #include "network/protocols/controller_events_protocol.hpp"
#include "network/protocols/game_events_protocol.hpp" #include "network/protocols/game_events_protocol.hpp"
@ -22,9 +21,7 @@ RaceEventManager::~RaceEventManager()
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** In network games this update function is called instead of /** In network games this update function is called instead of
* World::updateWorld(). This allow this function to postpone calling * World::updateWorld().
* the worl update while the countdown from the SynchronisationProtocol is
* running.
*/ */
void RaceEventManager::update(float dt) void RaceEventManager::update(float dt)
{ {
@ -33,18 +30,6 @@ void RaceEventManager::update(float dt)
if(!ProtocolManager::getInstance()) if(!ProtocolManager::getInstance())
return; return;
SynchronizationProtocol* protocol = static_cast<SynchronizationProtocol*>(
ProtocolManager::getInstance()->getProtocol(PROTOCOL_SYNCHRONIZATION));
if (protocol) // The existance of this protocol indicates that we play online
{
Log::debug("RaceEventManager", "Countdown value is %f",
protocol->getCountdown());
if (protocol->getCountdown() > 0.0)
{
return;
}
World::getWorld()->setNetworkWorld(true);
}
World::getWorld()->updateWorld(dt); World::getWorld()->updateWorld(dt);
// if the race is over // if the race is over