Added support for slowing down time on a client. This is used by

a server to reduce number of rewinds.
This commit is contained in:
hiker 2017-01-24 08:05:46 +11:00
parent 9b38e401f4
commit 241d31d8f9
4 changed files with 127 additions and 12 deletions

View File

@ -30,6 +30,7 @@
#include "network/network_config.hpp"
#include "network/protocols/client_lobby.hpp"
#include "network/protocols/server_lobby.hpp"
#include "network/rewind_manager.hpp"
#include "network/race_event_manager.hpp"
#include "tracks/track.hpp"
@ -61,6 +62,7 @@ WorldStatus::WorldStatus()
void WorldStatus::reset()
{
m_time = 0.0f;
m_adjust_time_by = 0.0f;
m_auxiliary_timer = 0.0f;
m_count_up_timer = 0.0f;
@ -391,14 +393,23 @@ void WorldStatus::updateTime(const float dt)
}
IrrlichtDevice *device = irr_driver->getDevice();
// If this is a client, the server might request an
// adjustment of this client's world clock (to reduce
// number of rewinds).
float actual_dt;
if (NetworkConfig::get()->isClient() &&
!RewindManager::get()->isRewinding())
actual_dt = adjustDT(dt);
else
actual_dt = dt;
switch (m_clock_mode)
{
case CLOCK_CHRONO:
if (!device->getTimer()->isStopped())
{
m_time += dt;
m_count_up_timer += dt;
m_time += actual_dt;
m_count_up_timer += actual_dt;
}
break;
case CLOCK_COUNTDOWN:
@ -412,8 +423,8 @@ void WorldStatus::updateTime(const float dt)
if (!device->getTimer()->isStopped())
{
m_time -= dt;
m_count_up_timer += dt;
m_time -= actual_dt;
m_count_up_timer += actual_dt;
}
if(m_time <= 0.0)
@ -427,6 +438,41 @@ void WorldStatus::updateTime(const float dt)
} // switch m_phase
} // update
//-----------------------------------------------------------------------------
/** If the server requests that a client's time should be adjusted,
* smoothly change the clock speed of this client to go a bit faster
* or slower till the overall adjustment was reached.
* \param dt Original time step size.
*/
float WorldStatus::adjustDT(float dt)
{
// If request, adjust world time to go ahead (adjust>0) or
// slow down (<0). This is done in 5% of dt steps so that the
// user will not notice this.
const float FRACTION = 0.05f; // fraction of dt to be adjusted
float time_adjust;
if (m_adjust_time_by >= 0) // make it run faster
{
time_adjust = dt * FRACTION;
if (time_adjust > m_adjust_time_by) time_adjust = m_adjust_time_by;
if (m_adjust_time_by > 0)
Log::verbose("info", "At %f %f adjusting time by %f dt %f to dt %f for %f",
World::getWorld()->getTime(), StkTime::getRealTime(),
time_adjust, dt, dt + time_adjust, m_adjust_time_by);
}
else // m_adjust_time negative, i.e. will go slower
{
time_adjust = -dt * FRACTION;
if (time_adjust < m_adjust_time_by) time_adjust = m_adjust_time_by;
Log::verbose("info", "At %f %f adjusting time by %f dt %f to dt %f for %f",
World::getWorld()->getTime(), StkTime::getRealTime(),
time_adjust, dt, dt + time_adjust, m_adjust_time_by);
}
m_adjust_time_by -= time_adjust;
dt += time_adjust;
return dt;
} // adjustDT
//-----------------------------------------------------------------------------
/** 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

View File

@ -103,6 +103,14 @@ private:
/** The third sound to be played in ready, set, go. */
SFXBase *m_start_sound;
/** In networked game the world clock might be adjusted (without the
* player noticing), e.g. if a client causes rewinds in the server,
* that client needs to speed up to be further ahead of the server
* and so reduce the number of rollbacks. This is the amount of time
* by which the client's clock needs to be adjusted (positive or
* negative). */
float m_adjust_time_by;
/** The clock mode: normal counting forwards, or countdown */
ClockType m_clock_mode;
protected:
@ -126,13 +134,15 @@ private:
float m_count_up_timer;
bool m_engines_started;
void startEngines();
/** 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. */
* 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. */
bool m_server_is_ready;
float adjustDT(float dt);
void startEngines();
public:
WorldStatus();
virtual ~WorldStatus();
@ -196,7 +206,10 @@ public:
float getTimeSinceStart() const { return m_count_up_timer; }
// ------------------------------------------------------------------------
void setReadyToRace() { m_server_is_ready = true; }
// ------------------------------------------------------------------------
/** Sets a time by which the clock should be adjusted. Used by networking
* if too many rewinds are detected. */
void setAdjustTime(float t) { m_adjust_time_by = t; }
}; // WorldStatus

View File

@ -69,7 +69,7 @@ void GameProtocol::update(float dt)
.addUInt32(a.m_value_l).addUInt32(a.m_value_r);
} // for a in m_all_actions
// FIXME: for now send reliable
// FIXME: for now send reliable
sendToServer(m_data_to_send, /*reliable*/ true);
m_all_actions.clear();
} // update
@ -87,6 +87,7 @@ bool GameProtocol::notifyEventAsynchronous(Event* event)
{
case GP_CONTROLLER_ACTION: handleControllerAction(event); break;
case GP_STATE: handleState(event); break;
case GP_ADJUST_TIME: handleAdjustTime(event); break;
default: Log::error("GameProtocol",
"Received unknown message type %d - ignored.",
message_type); break;
@ -130,6 +131,9 @@ void GameProtocol::controllerAction(int kart_id, PlayerAction action,
World::getWorld()->getTime(), action, value);
} // controllerAction
#include "utils/time.hpp"
// ----------------------------------------------------------------------------
/** Called when a controller event is receiver - either on the server from
* a client, or on a client from the server. It sorts the event into the
@ -140,9 +144,16 @@ void GameProtocol::handleControllerAction(Event *event)
{
NetworkString &data = event->data();
uint8_t count = data.getUInt8();
bool will_trigger_rewind = false;
float rewind_delta = 0.0f;
for (unsigned int i = 0; i < count; i++)
{
float time = data.getFloat();
if (time < World::getWorld()->getTime())
{
will_trigger_rewind = true;
rewind_delta = time - World::getWorld()->getTime();
}
uint8_t kart_id = data.getUInt8();
assert(kart_id < World::getWorld()->getNumKarts());
@ -168,10 +179,46 @@ void GameProtocol::handleControllerAction(Event *event)
// Send update to all clients except the original sender.
STKHost::get()->sendPacketExcept(event->getPeer(),
&data, false);
if (will_trigger_rewind)
{
Log::info("GameProtocol", "At %f %f requesting time adjust of %f for host %d",
World::getWorld()->getTime(), StkTime::getRealTime(),
rewind_delta, event->getPeer()->getHostId());
// This message from a client triggered a rewind in the server.
// To avoid this, signal to the client that it should slow down.
adjustTimeForClient(event->getPeer(), rewind_delta);
}
} // if server
} // handleControllerAction
// ----------------------------------------------------------------------------
/** The server might request that a client adjusts its world clock (in order to
* reduce rewinds). This function sends a a (unreliable) message to the
* client.
* \param peer The peer that triggered the rewind.
* \param t Time that the peer needs to slowdown (<0) or sped up(>0).
*/
void GameProtocol::adjustTimeForClient(STKPeer *peer, float t)
{
assert(NetworkConfig::get()->isServer());
NetworkString *ns = getNetworkString(5);
ns->addUInt8(GP_ADJUST_TIME).addFloat(t);
// This message can be send unreliable, it's not critical if it doesn't
// get delivered, the server will request again later anyway.
peer->sendPacket(ns, /*reliable*/false);
delete ns;
} // adjustTimeForClient
// ----------------------------------------------------------------------------
/** Called on a client when the server requests an adjustment of this client's
* world clock time (in order to reduce rewinds).
*/
void GameProtocol::handleAdjustTime(Event *event)
{
float t = event->data().getFloat();
World::getWorld()->setAdjustTime(t);
} // handleAdjustTime
// ----------------------------------------------------------------------------
/** Called by the server before assembling a new message containing the full
* state of the race to be sent to a client.

View File

@ -30,6 +30,7 @@
class BareNetworkString;
class NetworkString;
class STKPeer;
class GameProtocol : public Protocol
, public EventRewinder
@ -39,12 +40,18 @@ private:
/** The type of game events to be forwarded to the server. */
enum { GP_CONTROLLER_ACTION,
GP_STATE};
GP_STATE,
GP_ADJUST_TIME
};
/** A network string that collects all information from the server to be sent
* next. */
NetworkString *m_data_to_send;
/** The server might request that the world clock of a client is adjusted
* to reduce number of rollbacks. */
std::vector<int8_t> m_adjust_time;
// Dummy data structure to save all kart actions.
struct Action
{
@ -61,6 +68,7 @@ private:
void handleControllerAction(Event *event);
void handleState(Event *event);
void handleAdjustTime(Event *event);
public:
GameProtocol();
virtual ~GameProtocol();
@ -73,6 +81,7 @@ public:
void startNewState();
void addState(BareNetworkString *buffer);
void sendState();
void adjustTimeForClient(STKPeer *peer, float t);
virtual void undo(BareNetworkString *buffer) OVERRIDE;
virtual void rewind(BareNetworkString *buffer) OVERRIDE;
@ -80,7 +89,7 @@ public:
virtual void setup() OVERRIDE {};
// ------------------------------------------------------------------------
virtual void asynchronousUpdate() OVERRIDE {}
// ------------------------------------------------------------------------
}; // class GameProtocol
#endif // GAME_PROTOCOL_HPP