The server waits now for all client to start (which means it will be
behind the clients by the maximum latency, which in turn means that at server time T all client events at time T have arrived, so less rollback necessary).
This commit is contained in:
@@ -253,7 +253,11 @@ void WorldStatus::updateTime(const float dt)
|
||||
// Notify the clients that they can start ready-set-go
|
||||
if(NetworkConfig::get()->isServer())
|
||||
RaceEventManager::getInstance()->startReadySetGo();
|
||||
m_server_is_ready = true;
|
||||
// The server will wait in 'wait_for_server' for clients
|
||||
// to be ready before starting (using the same startReadySetGo
|
||||
// callback).
|
||||
if(!NetworkConfig::get()->isNetworking())
|
||||
m_server_is_ready = true;
|
||||
} // if not networked
|
||||
m_phase = WAIT_FOR_SERVER_PHASE;
|
||||
return; // Don't increase time
|
||||
@@ -264,6 +268,20 @@ void WorldStatus::updateTime(const float dt)
|
||||
// start the engines and then the race
|
||||
if(!m_server_is_ready) return;
|
||||
|
||||
if (NetworkConfig::get()->isNetworking() &&
|
||||
NetworkConfig::get()->isClient())
|
||||
{
|
||||
// Each client informs the server when its starting the race.
|
||||
// The server waits for the last message (i.e. from the slowest
|
||||
// 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;
|
||||
startEngines();
|
||||
|
||||
@@ -272,6 +290,7 @@ void WorldStatus::updateTime(const float dt)
|
||||
// (or state) of the finite state machine.
|
||||
return; // Don't increase time
|
||||
case READY_PHASE:
|
||||
|
||||
if (m_auxiliary_timer > 1.0)
|
||||
{
|
||||
if (m_play_ready_set_go_sounds)
|
||||
@@ -432,8 +451,11 @@ void WorldStatus::updateTime(const float dt)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Called on the client when it receives a notification from the server that
|
||||
* the server is now starting its ready-set-go phase. This function changes
|
||||
* the state of the finite state machine to be ready.
|
||||
* 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()
|
||||
{
|
||||
|
||||
@@ -39,6 +39,7 @@ GameEventsProtocol::~GameEventsProtocol()
|
||||
*/
|
||||
void GameEventsProtocol::setup()
|
||||
{
|
||||
m_count_ready_clients = 0;
|
||||
World::getWorld()->setReadyToRace();
|
||||
} // setup
|
||||
|
||||
@@ -63,16 +64,18 @@ bool GameEventsProtocol::notifyEvent(Event* event)
|
||||
int8_t type = data.getUInt8();
|
||||
switch (type)
|
||||
{
|
||||
case GE_ITEM_COLLECTED:
|
||||
collectedItem(data); break;
|
||||
case GE_KART_FINISHED_RACE:
|
||||
kartFinishedRace(data); break;
|
||||
case GE_START_READY_SET_GO:
|
||||
receivedReadySetGo(); break;
|
||||
case GE_START_READY_SET_GO:
|
||||
receivedReadySetGo(); break;
|
||||
case GE_CLIENT_STARTED_RSG:
|
||||
receivedClientHasStarted(event); break;
|
||||
case GE_ITEM_COLLECTED:
|
||||
collectedItem(data); break;
|
||||
case GE_KART_FINISHED_RACE:
|
||||
kartFinishedRace(data); break;
|
||||
|
||||
default:
|
||||
Log::warn("GameEventsProtocol", "Unkown message type.");
|
||||
break;
|
||||
default:
|
||||
Log::warn("GameEventsProtocol", "Unkown message type.");
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
} // notifyEvent
|
||||
@@ -187,3 +190,39 @@ void GameEventsProtocol::receivedReadySetGo()
|
||||
assert(NetworkConfig::get()->isClient());
|
||||
World::getWorld()->startReadySetGo();
|
||||
} // receivedReadySetGo
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Called on a client when it has started its ready-set-go. The client will
|
||||
* inform the server anout this. The server will wait for all clients to
|
||||
* have started before it will start its own countdown.
|
||||
*/
|
||||
void GameEventsProtocol::clientHasStarted()
|
||||
{
|
||||
assert(NetworkConfig::get()->isClient());
|
||||
NetworkString *ns = getNetworkString(1);
|
||||
ns->setSynchronous(true);
|
||||
ns->addUInt8(GE_CLIENT_STARTED_RSG);
|
||||
sendToServer(ns, /*reliable*/true);
|
||||
delete ns;
|
||||
} // clientHasStarted
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Called on the server when a client has signaled that it has started ready
|
||||
* set go. The server waits for the last client before it starts its own
|
||||
* ready set go. */
|
||||
void GameEventsProtocol::receivedClientHasStarted(Event *event)
|
||||
{
|
||||
assert(NetworkConfig::get()->isServer());
|
||||
m_count_ready_clients++;
|
||||
Log::verbose("GameEvent",
|
||||
"Host %d has started ready-set-go: %d out of %d done",
|
||||
event->getPeer()->getHostId(), m_count_ready_clients,
|
||||
STKHost::get()->getGameSetup()->getPlayerCount() );
|
||||
if (m_count_ready_clients==STKHost::get()->getGameSetup()->getPlayerCount())
|
||||
{
|
||||
Log::verbose("GameEvent", "All %d clients have started.",
|
||||
STKHost::get()->getGameSetup()->getPlayerCount());
|
||||
// SIgnal the server to start now - since it is now behind the client
|
||||
// times by the latency of the 'slowest' client.
|
||||
World::getWorld()->startReadySetGo();
|
||||
}
|
||||
} // receivedClientHasStarted
|
||||
|
||||
@@ -12,10 +12,18 @@ class GameEventsProtocol : public Protocol
|
||||
private:
|
||||
enum GameEventType {
|
||||
GE_START_READY_SET_GO = 0x01,
|
||||
GE_ITEM_COLLECTED = 0x02,
|
||||
GE_KART_FINISHED_RACE = 0x03
|
||||
GE_CLIENT_STARTED_RSG = 0x02,
|
||||
GE_ITEM_COLLECTED = 0x03,
|
||||
GE_KART_FINISHED_RACE = 0x04
|
||||
}; // GameEventType
|
||||
|
||||
/** Count how many clients have started 'ready'. The server
|
||||
* will only go to its 'ready' phase if all client shave done so.
|
||||
* This means the server time is far enough behind the clients
|
||||
* that at time T all client messages for time T have been
|
||||
* received (short of latency spikes). */
|
||||
int m_count_ready_clients;
|
||||
|
||||
public:
|
||||
GameEventsProtocol();
|
||||
virtual ~GameEventsProtocol();
|
||||
@@ -24,6 +32,8 @@ public:
|
||||
void collectedItem(Item* item, AbstractKart* kart);
|
||||
void collectedItem(const NetworkString &ns);
|
||||
void kartFinishedRace(AbstractKart *kart, float time);
|
||||
void clientHasStarted();
|
||||
void receivedClientHasStarted(Event *event);
|
||||
void kartFinishedRace(const NetworkString &ns);
|
||||
void startReadySetGo();
|
||||
void receivedReadySetGo();
|
||||
|
||||
@@ -116,6 +116,26 @@ void RaceEventManager::startReadySetGo()
|
||||
protocol->startReadySetGo();
|
||||
} // startReadySetGo
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Called on a client if it has started the 'ready' phase. The clients will
|
||||
* send this information to the server, and the server will wait for all
|
||||
* those messages before it starts its own 'ready set go'. De-facto that
|
||||
* means that the server will wait for the client with the highest latency
|
||||
* before it starts. Assuming no latency peaks that means that the server will
|
||||
* always have received all events for time T before it executes time T
|
||||
|
||||
The server waits
|
||||
* for all clients to report that they
|
||||
*/
|
||||
void RaceEventManager::clientHasStarted()
|
||||
{
|
||||
// this is only called in the client
|
||||
assert(NetworkConfig::get()->isClient());
|
||||
|
||||
GameEventsProtocol* protocol = static_cast<GameEventsProtocol*>(
|
||||
ProtocolManager::getInstance()->getProtocol(PROTOCOL_GAME_EVENTS));
|
||||
protocol->clientHasStarted();
|
||||
} // clientHasStarted
|
||||
// ----------------------------------------------------------------------------
|
||||
void RaceEventManager::controllerAction(Controller* controller,
|
||||
PlayerAction action, int value)
|
||||
{
|
||||
|
||||
@@ -57,6 +57,7 @@ public:
|
||||
int value);
|
||||
void kartFinishedRace(AbstractKart *kart, float time);
|
||||
void startReadySetGo();
|
||||
void clientHasStarted();
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns if this instance is in running state or not. */
|
||||
bool isRunning() { return m_running; }
|
||||
|
||||
Reference in New Issue
Block a user