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:
parent
9b38e401f4
commit
241d31d8f9
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user