Removed the storing of DT (which is not necessary when using ticks
now). Removed TimeStep information class etc.
This commit is contained in:
parent
17b3a79997
commit
2192d1383f
@ -1,5 +1,5 @@
|
|||||||
# Modify this file to change the last-modified date when you add/remove a file.
|
# Modify this file to change the last-modified date when you add/remove a file.
|
||||||
# This will then trigger a new cmake run automatically.
|
# This will then trigger a new cmake run automatically.
|
||||||
file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp")
|
file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp")
|
||||||
file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp")
|
file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp")
|
||||||
file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*")
|
file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*")
|
||||||
|
@ -300,7 +300,7 @@ void InputManager::handleStaticAction(int key, int value)
|
|||||||
fgets(s, 256, stdin);
|
fgets(s, 256, stdin);
|
||||||
int t;
|
int t;
|
||||||
StringUtils::fromString(s,t);
|
StringUtils::fromString(s,t);
|
||||||
RewindManager::get()->rewindTo(t);
|
RewindManager::get()->rewindTo(t, world->getTimeTicks());
|
||||||
Log::info("Rewind", "Rewinding from %d to %d",
|
Log::info("Rewind", "Rewinding from %d to %d",
|
||||||
world->getTimeTicks(), t);
|
world->getTimeTicks(), t);
|
||||||
}
|
}
|
||||||
|
@ -106,8 +106,8 @@ bool PlayerController::action(PlayerAction action, int value, bool dry_run)
|
|||||||
|
|
||||||
/** If dry_run (parameter) is true, this macro tests if this action would
|
/** If dry_run (parameter) is true, this macro tests if this action would
|
||||||
* trigger a state change in the specified variable (without actually
|
* trigger a state change in the specified variable (without actually
|
||||||
* doing it). If it will trigger a state change, the marco will trigger
|
* doing it). If it will trigger a state change, the macro will
|
||||||
* immediatley a return to the caller. If dry_run is false, it will only
|
* immediatley return to the caller. If dry_run is false, it will only
|
||||||
* assign the new value to the variable (and not return to the user
|
* assign the new value to the variable (and not return to the user
|
||||||
* early). The do-while(0) helps using this macro e.g. in the 'then'
|
* early). The do-while(0) helps using this macro e.g. in the 'then'
|
||||||
* clause of an if statement. */
|
* clause of an if statement. */
|
||||||
@ -125,7 +125,7 @@ bool PlayerController::action(PlayerAction action, int value, bool dry_run)
|
|||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
/** Basically the same as the above macro, but is uses getter/setter
|
/** Basically the same as the above macro, but is uses getter/setter
|
||||||
* funcitons. The name of the setter/getter is set'name'(value) and
|
* functions. The name of the setter/getter is set'name'(value) and
|
||||||
* get'name'(). */
|
* get'name'(). */
|
||||||
#define SET_OR_TEST_GETTER(name, value) \
|
#define SET_OR_TEST_GETTER(name, value) \
|
||||||
do \
|
do \
|
||||||
|
@ -154,9 +154,8 @@ float MainLoop::getLimitedDt()
|
|||||||
|
|
||||||
dt *= 0.001f;
|
dt *= 0.001f;
|
||||||
|
|
||||||
// If this is a client, the server might request an
|
// If this is a client, the server might request an adjustment of
|
||||||
// adjustment of this client's world clock (to reduce
|
// this client's world clock (to reduce number of rewinds).
|
||||||
// number of rewinds).
|
|
||||||
if (World::getWorld() &&
|
if (World::getWorld() &&
|
||||||
NetworkConfig::get()->isClient() &&
|
NetworkConfig::get()->isClient() &&
|
||||||
!RewindManager::get()->isRewinding() )
|
!RewindManager::get()->isRewinding() )
|
||||||
@ -297,14 +296,6 @@ void MainLoop::run()
|
|||||||
NetworkConfig::get()->unsetNetworking();
|
NetworkConfig::get()->unsetNetworking();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a Time step entry to the rewind list, which can store all
|
|
||||||
// all input ecents being issued during the driver update.
|
|
||||||
if (World::getWorld() && RewindManager::get()->isEnabled())
|
|
||||||
{
|
|
||||||
RewindManager::get()
|
|
||||||
->addNextTimeStep(World::getWorld()->getTimeTicks(), dt);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_abort)
|
if (!m_abort)
|
||||||
{
|
{
|
||||||
float frame_duration = num_steps * dt;
|
float frame_duration = num_steps * dt;
|
||||||
@ -338,16 +329,6 @@ void MainLoop::run()
|
|||||||
|
|
||||||
for(int i=0; i<num_steps; i++)
|
for(int i=0; i<num_steps; i++)
|
||||||
{
|
{
|
||||||
// Create the TimeStepInfo structure. For the first iteration
|
|
||||||
// this is done before the irr_driver update (since this needs
|
|
||||||
// to store input events at the event), but for any further
|
|
||||||
// substep another TimeStepInfo needs to be created here.
|
|
||||||
if (World::getWorld() && RewindManager::get()->isEnabled() && i>0)
|
|
||||||
{
|
|
||||||
RewindManager::get()
|
|
||||||
->addNextTimeStep(World::getWorld()->getTimeTicks(), dt);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable last substep in last iteration
|
// Enable last substep in last iteration
|
||||||
m_is_last_substep = (i == num_steps - 1);
|
m_is_last_substep = (i == num_steps - 1);
|
||||||
|
|
||||||
|
@ -139,6 +139,7 @@ void GameProtocol::controllerAction(int kart_id, PlayerAction action,
|
|||||||
BareNetworkString *s = new BareNetworkString(4);
|
BareNetworkString *s = new BareNetworkString(4);
|
||||||
s->addUInt8(kart_id).addUInt8(action).addUInt32(value)
|
s->addUInt8(kart_id).addUInt8(action).addUInt32(value)
|
||||||
.addUInt32(val_l).addUInt32(val_r);
|
.addUInt32(val_l).addUInt32(val_r);
|
||||||
|
|
||||||
RewindManager::get()->addEvent(this, s, /*confirmed*/true,
|
RewindManager::get()->addEvent(this, s, /*confirmed*/true,
|
||||||
World::getWorld()->getTimeTicks() );
|
World::getWorld()->getTimeTicks() );
|
||||||
|
|
||||||
@ -255,7 +256,9 @@ void GameProtocol::startNewState()
|
|||||||
} // startNewState
|
} // startNewState
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
/** Called by a server to add data to the current state.
|
/** Called by a server to add data to the current state. The data in buffer
|
||||||
|
* is copied, so the data can be freed after this call/.
|
||||||
|
* \param buffer Adds the data in the buffer to the current state.
|
||||||
*/
|
*/
|
||||||
void GameProtocol::addState(BareNetworkString *buffer)
|
void GameProtocol::addState(BareNetworkString *buffer)
|
||||||
{
|
{
|
||||||
@ -263,6 +266,7 @@ void GameProtocol::addState(BareNetworkString *buffer)
|
|||||||
m_data_to_send->addUInt16(buffer->size());
|
m_data_to_send->addUInt16(buffer->size());
|
||||||
(*m_data_to_send) += *buffer;
|
(*m_data_to_send) += *buffer;
|
||||||
} // addState
|
} // addState
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
/** Called when the last state information has been added and the message
|
/** Called when the last state information has been added and the message
|
||||||
* can be sent to the clients.
|
* can be sent to the clients.
|
||||||
|
@ -48,10 +48,6 @@ RewindInfoState::RewindInfoState(int ticks, Rewinder *rewinder,
|
|||||||
BareNetworkString *buffer, bool is_confirmed)
|
BareNetworkString *buffer, bool is_confirmed)
|
||||||
: RewindInfoRewinder(ticks, rewinder, buffer, is_confirmed)
|
: RewindInfoRewinder(ticks, rewinder, buffer, is_confirmed)
|
||||||
{
|
{
|
||||||
// rewinder = NULL is used in unit testing, in which case no world exists
|
|
||||||
if(rewinder!=NULL)
|
|
||||||
m_local_physics_time = Physics::getInstance()->getPhysicsWorld()
|
|
||||||
->getLocalTime();
|
|
||||||
} // RewindInfoState
|
} // RewindInfoState
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
@ -78,9 +78,6 @@ public:
|
|||||||
/** If this RewindInfo is an event. Subclasses will overwrite this. */
|
/** If this RewindInfo is an event. Subclasses will overwrite this. */
|
||||||
virtual bool isEvent() const { return false; }
|
virtual bool isEvent() const { return false; }
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
/** If this RewindInfo is time info. Subclasses will overwrite this. */
|
|
||||||
virtual bool isTime() const { return false; }
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
/** If this RewindInfo is an event. Subclasses will overwrite this. */
|
/** If this RewindInfo is an event. Subclasses will overwrite this. */
|
||||||
virtual bool isState() const { return false; }
|
virtual bool isState() const { return false; }
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
@ -121,18 +118,11 @@ public:
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
class RewindInfoState: public RewindInfoRewinder
|
class RewindInfoState: public RewindInfoRewinder
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
/** The 'left over' time from the physics. */
|
|
||||||
float m_local_physics_time;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RewindInfoState(int ticks, Rewinder *rewinder,
|
RewindInfoState(int ticks, Rewinder *rewinder,
|
||||||
BareNetworkString *buffer, bool is_confirmed);
|
BareNetworkString *buffer, bool is_confirmed);
|
||||||
virtual ~RewindInfoState() {};
|
virtual ~RewindInfoState() {};
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
/** Returns the left-over physics time. */
|
|
||||||
float getLocalPhysicsTime() const { return m_local_physics_time; }
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
virtual bool isState() const { return true; }
|
virtual bool isState() const { return true; }
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
#include "network/protocols/game_protocol.hpp"
|
#include "network/protocols/game_protocol.hpp"
|
||||||
#include "network/rewinder.hpp"
|
#include "network/rewinder.hpp"
|
||||||
#include "network/rewind_info.hpp"
|
#include "network/rewind_info.hpp"
|
||||||
#include "network/time_step_info.hpp"
|
|
||||||
#include "physics/physics.hpp"
|
#include "physics/physics.hpp"
|
||||||
#include "race/history.hpp"
|
#include "race/history.hpp"
|
||||||
#include "utils/log.hpp"
|
#include "utils/log.hpp"
|
||||||
@ -110,7 +109,7 @@ void RewindManager::addNextTimeStep(int time, float dt)
|
|||||||
if ((time > 0 || m_rewind_queue.isEmpty()) &&
|
if ((time > 0 || m_rewind_queue.isEmpty()) &&
|
||||||
World::getWorld()->getPhase() != WorldStatus::IN_GAME_MENU_PHASE)
|
World::getWorld()->getPhase() != WorldStatus::IN_GAME_MENU_PHASE)
|
||||||
{
|
{
|
||||||
m_rewind_queue.addNewTimeStep(time, dt);
|
// m_rewind_queue.addNewTimeStep(time, dt);
|
||||||
}
|
}
|
||||||
} // addNextTimeStep
|
} // addNextTimeStep
|
||||||
|
|
||||||
@ -122,8 +121,8 @@ void RewindManager::addNextTimeStep(int time, float dt)
|
|||||||
* \param buffer Pointer to the event data.
|
* \param buffer Pointer to the event data.
|
||||||
*/
|
*/
|
||||||
void RewindManager::addEvent(EventRewinder *event_rewinder,
|
void RewindManager::addEvent(EventRewinder *event_rewinder,
|
||||||
BareNetworkString *buffer, bool confirmed,
|
BareNetworkString *buffer, bool confirmed,
|
||||||
int ticks)
|
int ticks)
|
||||||
{
|
{
|
||||||
if (m_is_rewinding)
|
if (m_is_rewinding)
|
||||||
{
|
{
|
||||||
@ -166,7 +165,7 @@ void RewindManager::addNetworkState(int rewinder_index, BareNetworkString *buffe
|
|||||||
// On a client dt from a state is never used, it maintains
|
// On a client dt from a state is never used, it maintains
|
||||||
// its own dt information (using TimeEvents).
|
// its own dt information (using TimeEvents).
|
||||||
m_rewind_queue.addNetworkState(m_all_rewinder[rewinder_index], buffer,
|
m_rewind_queue.addNetworkState(m_all_rewinder[rewinder_index], buffer,
|
||||||
ticks, -99);
|
ticks);
|
||||||
} // addNetworkState
|
} // addNetworkState
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@ -192,24 +191,23 @@ void RewindManager::update(int ticks_not_used)
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
PROFILER_PUSH_CPU_MARKER("RewindManager - save state", 0x20, 0x7F, 0x20);
|
|
||||||
|
|
||||||
// Save state
|
// Save state
|
||||||
|
PROFILER_PUSH_CPU_MARKER("RewindManager - save state", 0x20, 0x7F, 0x20);
|
||||||
GameProtocol::lock()->startNewState();
|
GameProtocol::lock()->startNewState();
|
||||||
AllRewinder::const_iterator rewinder;
|
AllRewinder::const_iterator rewinder;
|
||||||
for (rewinder = m_all_rewinder.begin(); rewinder != m_all_rewinder.end(); ++rewinder)
|
for (rewinder = m_all_rewinder.begin(); rewinder != m_all_rewinder.end(); ++rewinder)
|
||||||
{
|
{
|
||||||
|
// TODO: check if it's worth passing in a sufficiently large buffer from
|
||||||
|
// GameProtocol - this would save the copy operation.
|
||||||
BareNetworkString *buffer = (*rewinder)->saveState();
|
BareNetworkString *buffer = (*rewinder)->saveState();
|
||||||
if (buffer && buffer->size() >= 0)
|
if (buffer && buffer->size() >= 0)
|
||||||
{
|
{
|
||||||
m_overall_state_size += buffer->size();
|
m_overall_state_size += buffer->size();
|
||||||
// Add to the previously created container
|
|
||||||
m_rewind_queue.addLocalState(*rewinder, buffer, /*confirmed*/true,
|
|
||||||
World::getWorld()->getTimeTicks());
|
|
||||||
GameProtocol::lock()->addState(buffer);
|
GameProtocol::lock()->addState(buffer);
|
||||||
} // size >= 0
|
} // size >= 0
|
||||||
else
|
delete buffer; // buffer can be freed
|
||||||
delete buffer; // NULL or 0 byte buffer
|
|
||||||
}
|
}
|
||||||
PROFILER_POP_CPU_MARKER();
|
PROFILER_POP_CPU_MARKER();
|
||||||
PROFILER_PUSH_CPU_MARKER("RewindManager - send state", 0x20, 0x7F, 0x40);
|
PROFILER_PUSH_CPU_MARKER("RewindManager - send state", 0x20, 0x7F, 0x40);
|
||||||
@ -218,12 +216,13 @@ void RewindManager::update(int ticks_not_used)
|
|||||||
m_last_saved_state = ticks;
|
m_last_saved_state = ticks;
|
||||||
} // update
|
} // update
|
||||||
|
|
||||||
|
#include "karts/abstract_kart.hpp"
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
/** Replays all events from the last event played till the specified time.
|
/** Replays all events from the last event played till the specified time.
|
||||||
* \param time Up to (and inclusive) which time events will be replayed.
|
* \param world_ticks Up to (and inclusive) which time events will be replayed.
|
||||||
* \param dt Number of time steps - should be 1.
|
* \param ticks Number of time steps - should be 1.
|
||||||
*/
|
*/
|
||||||
void RewindManager::playEventsTill(float time, int *ticks)
|
void RewindManager::playEventsTill(int world_ticks, int *ticks)
|
||||||
{
|
{
|
||||||
bool needs_rewind;
|
bool needs_rewind;
|
||||||
int rewind_ticks;
|
int rewind_ticks;
|
||||||
@ -232,43 +231,31 @@ void RewindManager::playEventsTill(float time, int *ticks)
|
|||||||
// time step.
|
// time step.
|
||||||
// merge and that have happened before the current time (which will
|
// merge and that have happened before the current time (which will
|
||||||
// be getTime()+dt - world time has not been updated yet).
|
// be getTime()+dt - world time has not been updated yet).
|
||||||
m_rewind_queue.mergeNetworkData(World::getWorld()->getTimeTicks(),
|
m_rewind_queue.mergeNetworkData(world_ticks, &needs_rewind, &rewind_ticks);
|
||||||
&needs_rewind, &rewind_ticks);
|
|
||||||
|
|
||||||
if (needs_rewind)
|
if (needs_rewind)
|
||||||
{
|
{
|
||||||
Log::setPrefix("Rewind");
|
Log::setPrefix("Rewind");
|
||||||
PROFILER_PUSH_CPU_MARKER("Rewind", 128, 128, 128);
|
PROFILER_PUSH_CPU_MARKER("Rewind", 128, 128, 128);
|
||||||
rewindTo(rewind_ticks);
|
if (World::getWorld()->getKart(0)->getControls().getAccel() > 0)
|
||||||
|
printf("");
|
||||||
|
rewindTo(rewind_ticks, world_ticks);
|
||||||
|
// This should replay everything up to 'now'
|
||||||
|
assert(World::getWorld()->getTimeTicks() == world_ticks);
|
||||||
PROFILER_POP_CPU_MARKER();
|
PROFILER_POP_CPU_MARKER();
|
||||||
Log::setPrefix("");
|
Log::setPrefix("");
|
||||||
TimeStepInfo *tsi = m_rewind_queue.getCurrent();
|
|
||||||
World::getWorld()->setTicks(tsi->getTicks());
|
|
||||||
Physics::getInstance()->getPhysicsWorld()->resetLocalTime();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(!m_is_rewinding);
|
||||||
if (m_rewind_queue.isEmpty()) return;
|
if (m_rewind_queue.isEmpty()) return;
|
||||||
|
|
||||||
// This is necessary to avoid that rewinding an event will store the
|
// This is necessary to avoid that rewinding an event will store the
|
||||||
// event again as a seemingly new event.
|
// event again as a seemingly new event.
|
||||||
assert(!m_is_rewinding);
|
|
||||||
m_is_rewinding = true;
|
m_is_rewinding = true;
|
||||||
|
|
||||||
// Now play all events between time and time + dt, i.e. all events
|
// Now play all events that happened at the current time stamp.
|
||||||
// stored at the last TimeStep info in the rewind queue.
|
m_rewind_queue.replayAllEvents(world_ticks);
|
||||||
//assert(m_rewind_queue.getLast() == m_rewind_queue.getCurrent());
|
|
||||||
|
|
||||||
TimeStepInfo *tsi = m_rewind_queue.getLast();
|
|
||||||
|
|
||||||
// ++m_rewind_queue; // Point to end of queue now
|
|
||||||
tsi->replayAllEvents();
|
|
||||||
|
|
||||||
if (tsi->hasConfirmedState() && NetworkConfig::get()->isClient())
|
|
||||||
{
|
|
||||||
Log::warn("RewindManager",
|
|
||||||
"Client has received state in the future: at %d state %d",
|
|
||||||
World::getWorld()->getTimeTicks(), tsi->getTicks());
|
|
||||||
}
|
|
||||||
m_is_rewinding = false;
|
m_is_rewinding = false;
|
||||||
} // playEventsTill
|
} // playEventsTill
|
||||||
|
|
||||||
@ -277,8 +264,11 @@ void RewindManager::playEventsTill(float time, int *ticks)
|
|||||||
* World::getTime() is reached again: it will replay everything before
|
* World::getTime() is reached again: it will replay everything before
|
||||||
* World::getTime(), but not the events at World::getTime() (or later)/
|
* World::getTime(), but not the events at World::getTime() (or later)/
|
||||||
* \param rewind_ticks Time to rewind to.
|
* \param rewind_ticks Time to rewind to.
|
||||||
|
* \param now_ticks Up to which ticks events are replayed: up to but
|
||||||
|
* EXCLUDING new_ticks (the event at now_ticks are played in
|
||||||
|
* the calling subroutine playEventsTill).
|
||||||
*/
|
*/
|
||||||
void RewindManager::rewindTo(int rewind_ticks)
|
void RewindManager::rewindTo(int rewind_ticks, int now_ticks)
|
||||||
{
|
{
|
||||||
assert(!m_is_rewinding);
|
assert(!m_is_rewinding);
|
||||||
bool is_history = history->replayHistory();
|
bool is_history = history->replayHistory();
|
||||||
@ -297,26 +287,17 @@ void RewindManager::rewindTo(int rewind_ticks)
|
|||||||
// Then undo the rewind infos going backwards in time
|
// Then undo the rewind infos going backwards in time
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
m_is_rewinding = true;
|
m_is_rewinding = true;
|
||||||
m_rewind_queue.undoUntil(rewind_ticks);
|
|
||||||
|
// This will go back till the first confirmed state is found before
|
||||||
|
// the specified rewind ticks.
|
||||||
|
int exact_rewind_ticks = m_rewind_queue.undoUntil(rewind_ticks);
|
||||||
|
|
||||||
// Rewind the required state(s)
|
// Rewind the required state(s)
|
||||||
// ----------------------------
|
// ----------------------------
|
||||||
World *world = World::getWorld();
|
World *world = World::getWorld();
|
||||||
float current_time = world->getTime();
|
|
||||||
|
|
||||||
// Get the (first) full state to which we have to rewind
|
|
||||||
TimeStepInfo *current = m_rewind_queue.getCurrent();
|
|
||||||
|
|
||||||
// Store the time to which we have to replay to,
|
|
||||||
// which can be earlier than rewind_ticks
|
|
||||||
int exact_rewind_ticks = current->getTicks();
|
|
||||||
|
|
||||||
// Now start the rewind with the full state:
|
// Now start the rewind with the full state:
|
||||||
world->setTicks(exact_rewind_ticks);
|
world->setTicks(exact_rewind_ticks);
|
||||||
float local_physics_time = current->getLocalPhysicsTime();
|
|
||||||
Physics::getInstance()->getPhysicsWorld()->setLocalTime(local_physics_time);
|
|
||||||
|
|
||||||
float dt = -1.0f;
|
|
||||||
|
|
||||||
// Need to exit loop if in-game menu is open, since world clock
|
// Need to exit loop if in-game menu is open, since world clock
|
||||||
// will not be increased while the game is paused
|
// will not be increased while the game is paused
|
||||||
@ -327,30 +308,34 @@ void RewindManager::rewindTo(int rewind_ticks)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore state from the current time
|
// Get the (first) full state to which we have to rewind
|
||||||
current->replayAllStates();
|
RewindInfo *current = m_rewind_queue.getCurrent();
|
||||||
|
assert(current->isState());
|
||||||
|
|
||||||
// Now go forward through the list of rewind infos. A new timestep
|
// Restore states from the exact rewind time
|
||||||
// info for the current time has already been added previously, so
|
// -----------------------------------------
|
||||||
// we rewind till we have reached the last timestep entry (which is
|
// A loop in case that we should split states into several smaller ones:
|
||||||
// the current time step).
|
while (current && current->getTicks() == exact_rewind_ticks &&
|
||||||
while (current != m_rewind_queue.getLast())
|
current->isState() )
|
||||||
{
|
{
|
||||||
// Now handle all events(!) at the current time (i.e. between
|
current->rewind();
|
||||||
// World::getTime() and World::getTime()+dt) before updating
|
m_rewind_queue.next();
|
||||||
// the world:
|
current = m_rewind_queue.getCurrent();
|
||||||
current->replayAllEvents();
|
}
|
||||||
dt = current->getDT();
|
|
||||||
|
// Now go forward through the list of rewind infos till we reach 'now':
|
||||||
|
while(world->getTimeTicks() < now_ticks )
|
||||||
|
{
|
||||||
|
m_rewind_queue.replayAllEvents(world->getTimeTicks());
|
||||||
|
|
||||||
|
// Now simulate the next time step
|
||||||
world->updateWorld(1);
|
world->updateWorld(1);
|
||||||
#undef SHOW_ROLLBACK
|
#undef SHOW_ROLLBACK
|
||||||
#ifdef SHOW_ROLLBACK
|
#ifdef SHOW_ROLLBACK
|
||||||
irr_driver->update(dt);
|
irr_driver->update(stk_config->ticks2Time(1));
|
||||||
#endif
|
#endif
|
||||||
world->updateTime(1);
|
world->updateTime(1);
|
||||||
|
|
||||||
++m_rewind_queue;
|
|
||||||
current = m_rewind_queue.getCurrent();
|
|
||||||
world->setTicks(current->getTicks());
|
|
||||||
} // while (world->getTicks() < current_ticks)
|
} // while (world->getTicks() < current_ticks)
|
||||||
|
|
||||||
// Now compute the errors which need to be visually smoothed
|
// Now compute the errors which need to be visually smoothed
|
||||||
|
@ -138,8 +138,8 @@ public:
|
|||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
void update(int ticks);
|
void update(int ticks);
|
||||||
void rewindTo(int target_ticks);
|
void rewindTo(int target_ticks, int ticks_now);
|
||||||
void playEventsTill(float time, int *ticks);
|
void playEventsTill(int world_ticks, int *ticks);
|
||||||
void addEvent(EventRewinder *event_rewinder, BareNetworkString *buffer,
|
void addEvent(EventRewinder *event_rewinder, BareNetworkString *buffer,
|
||||||
bool confirmed, int ticks = -1);
|
bool confirmed, int ticks = -1);
|
||||||
void addNetworkEvent(EventRewinder *event_rewinder,
|
void addNetworkEvent(EventRewinder *event_rewinder,
|
||||||
|
@ -23,7 +23,6 @@
|
|||||||
#include "network/network_config.hpp"
|
#include "network/network_config.hpp"
|
||||||
#include "network/rewind_info.hpp"
|
#include "network/rewind_info.hpp"
|
||||||
#include "network/rewind_manager.hpp"
|
#include "network/rewind_manager.hpp"
|
||||||
#include "network/time_step_info.hpp"
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
@ -43,7 +42,6 @@
|
|||||||
*/
|
*/
|
||||||
RewindQueue::RewindQueue()
|
RewindQueue::RewindQueue()
|
||||||
{
|
{
|
||||||
m_current = m_time_step_info.begin();
|
|
||||||
reset();
|
reset();
|
||||||
} // RewindQueue
|
} // RewindQueue
|
||||||
|
|
||||||
@ -53,14 +51,8 @@ RewindQueue::RewindQueue()
|
|||||||
*/
|
*/
|
||||||
RewindQueue::~RewindQueue()
|
RewindQueue::~RewindQueue()
|
||||||
{
|
{
|
||||||
// Destroying the
|
// This frees all current data
|
||||||
AllTimeStepInfo::const_iterator i;
|
reset();
|
||||||
|
|
||||||
for(i=m_time_step_info.begin(); i!=m_time_step_info.end(); ++i)
|
|
||||||
{
|
|
||||||
delete *i;
|
|
||||||
}
|
|
||||||
m_time_step_info.clear();
|
|
||||||
} // ~RewindQueue
|
} // ~RewindQueue
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@ -68,16 +60,6 @@ RewindQueue::~RewindQueue()
|
|||||||
*/
|
*/
|
||||||
void RewindQueue::reset()
|
void RewindQueue::reset()
|
||||||
{
|
{
|
||||||
|
|
||||||
for(AllTimeStepInfo::const_iterator i =m_time_step_info.begin();
|
|
||||||
i!=m_time_step_info.end(); i++)
|
|
||||||
{
|
|
||||||
delete *i;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_time_step_info.clear();
|
|
||||||
m_current = m_time_step_info.begin();
|
|
||||||
|
|
||||||
m_network_events.lock();
|
m_network_events.lock();
|
||||||
|
|
||||||
AllNetworkRewindInfo &info = m_network_events.getData();
|
AllNetworkRewindInfo &info = m_network_events.getData();
|
||||||
@ -88,87 +70,50 @@ void RewindQueue::reset()
|
|||||||
}
|
}
|
||||||
m_network_events.getData().clear();
|
m_network_events.getData().clear();
|
||||||
m_network_events.unlock();
|
m_network_events.unlock();
|
||||||
|
|
||||||
|
AllRewindInfo::const_iterator i;
|
||||||
|
for (i = m_all_rewind_info.begin(); i != m_all_rewind_info.end(); ++i)
|
||||||
|
delete *i;
|
||||||
|
|
||||||
|
m_all_rewind_info.clear();
|
||||||
|
m_current = m_all_rewind_info.end();
|
||||||
} // reset
|
} // reset
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
/** Adds a new TimeStepInfo for the specified time. The TimeStepInfo acts
|
|
||||||
* as an container to store all states and events that happen at this time
|
|
||||||
* (or at least close to this time, since e.g. several events from clients
|
|
||||||
* happening at slightly different times will be all handled in the same
|
|
||||||
* timestep.
|
|
||||||
* \param time New time to add.
|
|
||||||
* \param dt Time step size that is going to be used for this time step.
|
|
||||||
*/
|
|
||||||
void RewindQueue::addNewTimeStep(int ticks, float dt)
|
|
||||||
{
|
|
||||||
TimeStepInfo *tsi = new TimeStepInfo(ticks, dt);
|
|
||||||
assert(m_time_step_info.empty() ||
|
|
||||||
ticks > m_time_step_info.back()->getTicks() );
|
|
||||||
m_time_step_info.push_back(tsi);
|
|
||||||
|
|
||||||
// If current was not initialised
|
|
||||||
if (m_current == m_time_step_info.end())
|
|
||||||
{
|
|
||||||
m_current--;
|
|
||||||
}
|
|
||||||
} // addNewTimeStep
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
/** Finds the TimeStepInfo object to which an event at time t should be added.
|
|
||||||
* The TimeStepInfo object might not have the exacct same time, it can be
|
|
||||||
* the closest existing (or in future this function might even add a totally
|
|
||||||
* new TimeStepInfo object).
|
|
||||||
* \param Time at which the event that needs to be added hapened.
|
|
||||||
*/
|
|
||||||
RewindQueue::AllTimeStepInfo::iterator
|
|
||||||
RewindQueue::findPreviousTimeStepInfo(int ticks)
|
|
||||||
{
|
|
||||||
AllTimeStepInfo::iterator i = m_time_step_info.end();
|
|
||||||
while(i!=m_time_step_info.begin())
|
|
||||||
{
|
|
||||||
i--;
|
|
||||||
if ((*i)->getTicks() <= ticks) return i;
|
|
||||||
}
|
|
||||||
return i;
|
|
||||||
} // findPreviousTimeStepInfo
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
/** A compare function used when sorting the event lists. It sorts events by
|
|
||||||
* time. In case of equal times, it sorts states and events first (since the
|
|
||||||
* state needs to be restored when replaying first before any other events).
|
|
||||||
*/
|
|
||||||
bool RewindQueue::_TimeStepInfoCompare::operator()(const TimeStepInfo * const ri1,
|
|
||||||
const TimeStepInfo * const ri2) const
|
|
||||||
{
|
|
||||||
return ri1->getTicks() < ri2->getTicks();
|
|
||||||
} // RewindQueue::operator()
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
/** Inserts a RewindInfo object in the list of all events at the correct time.
|
/** Inserts a RewindInfo object in the list of all events at the correct time.
|
||||||
* If there are several RewindInfo at the exact same time, state RewindInfo
|
* If there are several RewindInfo at the exact same time, state RewindInfo
|
||||||
* will be insert at the front, and event and time info at the end of the
|
* will be insert at the front, and event info at the end of the RewindInfo
|
||||||
* RewindInfo with the same time.
|
* with the same time.
|
||||||
* \param ri The RewindInfo object to insert.
|
* \param ri The RewindInfo object to insert.
|
||||||
|
* \param update_current If set, the current pointer will be updated if
|
||||||
|
* necessary to point to the new event
|
||||||
*/
|
*/
|
||||||
void RewindQueue::insertRewindInfo(RewindInfo *ri)
|
void RewindQueue::insertRewindInfo(RewindInfo *ri)
|
||||||
{
|
{
|
||||||
// FIXME: this should always be the last element in the list(??)
|
AllRewindInfo::iterator i = m_all_rewind_info.end();
|
||||||
AllTimeStepInfo::iterator bucket = findPreviousTimeStepInfo(ri->getTicks());
|
|
||||||
|
|
||||||
// FIXME: In case of a history replay an element could be inserted in the
|
while (i != m_all_rewind_info.begin())
|
||||||
// very first frame (on very quick recorded start, and if the first frame
|
{
|
||||||
// takes a long time - e.g. in networking startup), i.e. before a TimeStep
|
AllRewindInfo::iterator i_prev = i;
|
||||||
// info was added. Since this is mostly for debugging, just ignore this
|
i_prev--;
|
||||||
// this for now.
|
// Now test if 'ri' needs to be inserted after the
|
||||||
if(bucket!=m_time_step_info.end())
|
// previous element, i.e. before the current element:
|
||||||
(*bucket)->insert(ri);
|
if ((*i_prev)->getTicks() < ri->getTicks()) break;
|
||||||
|
if ((*i_prev)->getTicks() == ri->getTicks() &&
|
||||||
|
(*i_prev)->isState() && ri->isEvent() ) break;
|
||||||
|
i = i_prev;
|
||||||
|
}
|
||||||
|
if(m_current == m_all_rewind_info.end())
|
||||||
|
m_current = m_all_rewind_info.insert(i, ri);
|
||||||
|
else
|
||||||
|
m_all_rewind_info.insert(i, ri);
|
||||||
} // insertRewindInfo
|
} // insertRewindInfo
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
/** Adds an event to the rewind data. The data to be stored must be allocated
|
/** Adds an event to the rewind data. The data to be stored must be allocated
|
||||||
* and not freed by the caller!
|
* and not freed by the caller!
|
||||||
* \param time Time at which the event was recorded.
|
|
||||||
* \param buffer Pointer to the event data.
|
* \param buffer Pointer to the event data.
|
||||||
|
* \param ticks Time at which the event happened.
|
||||||
*/
|
*/
|
||||||
void RewindQueue::addLocalEvent(EventRewinder *event_rewinder,
|
void RewindQueue::addLocalEvent(EventRewinder *event_rewinder,
|
||||||
BareNetworkString *buffer, bool confirmed,
|
BareNetworkString *buffer, bool confirmed,
|
||||||
@ -188,7 +133,7 @@ void RewindQueue::addLocalEvent(EventRewinder *event_rewinder,
|
|||||||
* \param confirmed If this state is confirmed to be correct (e.g. is
|
* \param confirmed If this state is confirmed to be correct (e.g. is
|
||||||
* being received from the servrer), or just a local state for
|
* being received from the servrer), or just a local state for
|
||||||
* faster rewinds.
|
* faster rewinds.
|
||||||
* \param time Time at which the state was captured.
|
* \param ticks Time at which the event happened.
|
||||||
*/
|
*/
|
||||||
void RewindQueue::addLocalState(Rewinder *rewinder, BareNetworkString *buffer,
|
void RewindQueue::addLocalState(Rewinder *rewinder, BareNetworkString *buffer,
|
||||||
bool confirmed, int ticks)
|
bool confirmed, int ticks)
|
||||||
@ -201,10 +146,10 @@ void RewindQueue::addLocalState(Rewinder *rewinder, BareNetworkString *buffer,
|
|||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
/** Adds an event to the list of network rewind data. This function is
|
/** Adds an event to the list of network rewind data. This function is
|
||||||
* threadsafe so can be called by the network thread. The data is synched
|
* threadsafe so can be called by the network thread. The data is synched
|
||||||
* to m_time_step_info by the main thread. The data to be stored must be
|
* to m_tRewindInformation list by the main thread. The data to be stored
|
||||||
* allocated and not freed by the caller!
|
* must be allocated and not freed by the caller!
|
||||||
* \param time Time at which the event was recorded.
|
|
||||||
* \param buffer Pointer to the event data.
|
* \param buffer Pointer to the event data.
|
||||||
|
* \param ticks Time at which the event happened.
|
||||||
*/
|
*/
|
||||||
void RewindQueue::addNetworkEvent(EventRewinder *event_rewinder,
|
void RewindQueue::addNetworkEvent(EventRewinder *event_rewinder,
|
||||||
BareNetworkString *buffer, int ticks)
|
BareNetworkString *buffer, int ticks)
|
||||||
@ -220,13 +165,13 @@ void RewindQueue::addNetworkEvent(EventRewinder *event_rewinder,
|
|||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
/** Adds a state to the list of network rewind data. This function is
|
/** Adds a state to the list of network rewind data. This function is
|
||||||
* threadsafe so can be called by the network thread. The data is synched
|
* threadsafe so can be called by the network thread. The data is synched
|
||||||
* to m_time_step_info by the main thread. The data to be stored must be
|
* to RewindInfo list by the main thread. The data to be stored must be
|
||||||
* allocated and not freed by the caller!
|
* allocated and not freed by the caller!
|
||||||
* \param time Time at which the event was recorded.
|
|
||||||
* \param buffer Pointer to the event data.
|
* \param buffer Pointer to the event data.
|
||||||
|
* \param ticks Time at which the event happened.
|
||||||
*/
|
*/
|
||||||
void RewindQueue::addNetworkState(Rewinder *rewinder, BareNetworkString *buffer,
|
void RewindQueue::addNetworkState(Rewinder *rewinder, BareNetworkString *buffer,
|
||||||
int ticks, float dt)
|
int ticks)
|
||||||
{
|
{
|
||||||
RewindInfo *ri = new RewindInfoState(ticks, rewinder,
|
RewindInfo *ri = new RewindInfoState(ticks, rewinder,
|
||||||
buffer, /*confirmed*/true);
|
buffer, /*confirmed*/true);
|
||||||
@ -237,19 +182,18 @@ void RewindQueue::addNetworkState(Rewinder *rewinder, BareNetworkString *buffer,
|
|||||||
} // addNetworkState
|
} // addNetworkState
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
/** Merges thread-safe all data received from the network with the current
|
/** Merges thread-safe all data received from the network up to and including
|
||||||
* local rewind information.
|
* the current time (tick) with the current local rewind information.
|
||||||
* \param world_ticks[in] Current world time up to which network events will be
|
* \param world_ticks[in] Current world time up to which network events will be
|
||||||
* merged in.
|
* merged in.
|
||||||
* \param needs_rewind[out] True if network rewind information was received
|
* \param needs_rewind[out] True if a network event/state was received
|
||||||
* which was in the past (of this simulation), so a rewind must be
|
* which was in the past (of this simulation), so a rewind must be
|
||||||
* performed.
|
* performed.
|
||||||
* \param rewind_time[out] If needs_rewind is true, the time to which a rewind
|
* \param rewind_time[out] If needs_rewind is true, the time to which a rewind
|
||||||
* must be performed (at least). Otherwise undefined, but the value
|
* must be performed (at least). Otherwise undefined.
|
||||||
* might be modified in this function.
|
|
||||||
*/
|
*/
|
||||||
void RewindQueue::mergeNetworkData(int world_ticks,
|
void RewindQueue::mergeNetworkData(int world_ticks, bool *needs_rewind,
|
||||||
bool *needs_rewind, int *rewind_ticks)
|
int *rewind_ticks)
|
||||||
{
|
{
|
||||||
*needs_rewind = false;
|
*needs_rewind = false;
|
||||||
m_network_events.lock();
|
m_network_events.lock();
|
||||||
@ -268,10 +212,9 @@ void RewindQueue::mergeNetworkData(int world_ticks,
|
|||||||
// FIXME: making m_network_events sorted would prevent the need to
|
// FIXME: making m_network_events sorted would prevent the need to
|
||||||
// go through the whole list of events
|
// go through the whole list of events
|
||||||
AllNetworkRewindInfo::iterator i = m_network_events.getData().begin();
|
AllNetworkRewindInfo::iterator i = m_network_events.getData().begin();
|
||||||
while( i!=m_network_events.getData().end() )
|
while (i != m_network_events.getData().end())
|
||||||
{
|
{
|
||||||
// Ignore any events that will happen in the future. An event needs
|
// Ignore any events that will happen in the future. The current
|
||||||
// to be handled at the closest time to its original time. The current
|
|
||||||
// time step is world_ticks.
|
// time step is world_ticks.
|
||||||
if ((*i)->getTicks() > world_ticks)
|
if ((*i)->getTicks() > world_ticks)
|
||||||
{
|
{
|
||||||
@ -285,64 +228,36 @@ void RewindQueue::mergeNetworkData(int world_ticks,
|
|||||||
if (NetworkConfig::get()->isServer() && (*i)->getTicks() < world_ticks)
|
if (NetworkConfig::get()->isServer() && (*i)->getTicks() < world_ticks)
|
||||||
{
|
{
|
||||||
Log::warn("RewindQueue", "At %d received message from %d",
|
Log::warn("RewindQueue", "At %d received message from %d",
|
||||||
world_ticks, (*i)->getTicks());
|
world_ticks, (*i)->getTicks());
|
||||||
// Server received an event in the past. Adjust this event
|
// Server received an event in the past. Adjust this event
|
||||||
// to be executed now - at least we get a bit closer to the
|
// to be executed 'now' - at least we get a bit closer to the
|
||||||
// client state.
|
// client state.
|
||||||
(*i)->setTicks(world_ticks);
|
(*i)->setTicks(world_ticks);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find closest previous time step.
|
insertRewindInfo(*i);
|
||||||
AllTimeStepInfo::iterator prev =
|
|
||||||
findPreviousTimeStepInfo((*i)->getTicks());
|
|
||||||
AllTimeStepInfo::iterator next = prev;
|
|
||||||
next++;
|
|
||||||
|
|
||||||
int event_ticks = (*i)->getTicks();
|
Log::info("Rewind", "Inserting %s from time %d",
|
||||||
|
(*i)->isEvent() ? "event" : "state",
|
||||||
|
(*i)->getTicks() );
|
||||||
|
|
||||||
TimeStepInfo *tsi;
|
// Check if a rewind is necessary, i.e. a message is received in the
|
||||||
|
// past of client (server never rewinds).
|
||||||
// Assign this event to the closest of the two existing timesteps
|
if (NetworkConfig::get()->isClient() && (*i)->getTicks() < world_ticks)
|
||||||
// prev and next (inserting an additional event in the past would
|
|
||||||
// mean more CPU work in the rewind this will very likely trigger).
|
|
||||||
if (next == m_time_step_info.end())
|
|
||||||
tsi = *prev;
|
|
||||||
else if ( (*next)->getTicks()-event_ticks < event_ticks-(*prev)->getTicks() )
|
|
||||||
tsi = *next;
|
|
||||||
else
|
|
||||||
tsi = *prev;
|
|
||||||
|
|
||||||
tsi->insert(*i);
|
|
||||||
Log::info("Rewind", "Inserting event from time %d type %c to timstepinfo %d prev %d next %d",
|
|
||||||
(*i)->getTicks(),
|
|
||||||
(*i)->isEvent() ? 'E' : ((*i)->isState() ? 'S' : 'T'),
|
|
||||||
tsi->getTicks(),
|
|
||||||
(*prev)->getTicks(),
|
|
||||||
next != m_time_step_info.end() ? (*next)->getTicks() : 9999 );
|
|
||||||
|
|
||||||
// Check if a rewind is necessary: either an message arrived in the past
|
|
||||||
// or if the time is between world_time and world_time+dt (otherwise
|
|
||||||
// the message would have been ignored further up), 'rewind' to this new
|
|
||||||
// state anyway
|
|
||||||
if (NetworkConfig::get()->isClient())
|
|
||||||
{
|
{
|
||||||
// We need rewind if we either receive an event in the past
|
// We need rewind if we receive an event in the past. This will
|
||||||
// (FIXME: maybe we can just ignore this since we will also get
|
// then trigger a rewind later. Note that we only rewind to the
|
||||||
// a state update??), or receive a state from the current time
|
// latest event that happened earlier than 'now' - if there is
|
||||||
// (i.e. between world_time and world_time+dt). In the latter
|
// more than one event in the past, we rewind to the last event.
|
||||||
// case we can just 'rewind' to this stage instead of doing a
|
// Since we restore a state before the rewind, this state will
|
||||||
// full simulation - though this client should potentially
|
// either include the earlier event or the state will be before
|
||||||
// speed up a bit: if it receives a state from the server
|
// the earlier event, and the event will be replayed anyway. This
|
||||||
// at the time the client is currently simulating (instead of
|
// makes it easy to handle lost event messages.
|
||||||
// triggering a rollback) it is not ahead enough of the server
|
*needs_rewind = true;
|
||||||
// which will trigger a time adjustment from the server anyway.
|
if ((*i)->getTicks() > *rewind_ticks)
|
||||||
if (tsi->getTicks() < world_ticks ||
|
*rewind_ticks = (*i)->getTicks();
|
||||||
(*i)->isState() && tsi == m_time_step_info.back())
|
} // if client and ticks < world_ticks
|
||||||
{
|
|
||||||
*needs_rewind = true;
|
|
||||||
if (tsi->getTicks() > *rewind_ticks) *rewind_ticks = tsi->getTicks();
|
|
||||||
}
|
|
||||||
} // if client
|
|
||||||
i = m_network_events.getData().erase(i);
|
i = m_network_events.getData().erase(i);
|
||||||
} // for i in m_network_events
|
} // for i in m_network_events
|
||||||
|
|
||||||
@ -353,7 +268,7 @@ void RewindQueue::mergeNetworkData(int world_ticks,
|
|||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
bool RewindQueue::isEmpty() const
|
bool RewindQueue::isEmpty() const
|
||||||
{
|
{
|
||||||
return m_time_step_info.empty();
|
return m_current == m_all_rewind_info.end();
|
||||||
} // isEmpty
|
} // isEmpty
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@ -361,53 +276,60 @@ bool RewindQueue::isEmpty() const
|
|||||||
*/
|
*/
|
||||||
bool RewindQueue::hasMoreRewindInfo() const
|
bool RewindQueue::hasMoreRewindInfo() const
|
||||||
{
|
{
|
||||||
return m_current != m_time_step_info.end();
|
return m_current != m_all_rewind_info.end();
|
||||||
} // hasMoreRewindInfo
|
} // hasMoreRewindInfo
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
/** Determines the next time step size to use when recomputing the physics.
|
|
||||||
* The time step size is either 1/60 (default physics), or less, if there
|
|
||||||
* is an even to handle before that time.
|
|
||||||
* \param next_state The next state to replay.
|
|
||||||
* \param end_time The end time to which we must replay forward. Don't
|
|
||||||
* return a dt that would be bigger tham this value.
|
|
||||||
* \return The time step size to use in the next simulation step.
|
|
||||||
*/
|
|
||||||
float RewindQueue::determineNextDT()
|
|
||||||
{
|
|
||||||
return stk_config->ticks2Time(1);
|
|
||||||
} // determineNextDT
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
/** Rewinds the rewind queue and undos all events/states stored. It stops
|
/** Rewinds the rewind queue and undos all events/states stored. It stops
|
||||||
* when the first confirmed state is reached that was recorded before the
|
* when the first confirmed state is reached that was recorded before the
|
||||||
* undo_time and sets the internal 'current' pointer to this state. It is
|
* undo_time and sets the internal 'current' pointer to this state.
|
||||||
* assumed that this function is called after a new TimeStepInfo instance
|
|
||||||
* was added (i.e. after RewindManager::update() was called), so the state
|
|
||||||
* m_current is pointing to is ignored.
|
|
||||||
* \param undo_time To what at least events need to be undone.
|
* \param undo_time To what at least events need to be undone.
|
||||||
|
* \return The time in ticks of the confirmed state
|
||||||
*/
|
*/
|
||||||
void RewindQueue::undoUntil(int undo_ticks)
|
int RewindQueue::undoUntil(int undo_ticks)
|
||||||
{
|
{
|
||||||
while (m_current != m_time_step_info.begin())
|
// m_current points to the next not yet executed event (or state)
|
||||||
|
// or end() if nothing else is in the queue
|
||||||
|
if (m_current != m_all_rewind_info.begin())
|
||||||
|
m_current--;
|
||||||
|
|
||||||
|
do
|
||||||
{
|
{
|
||||||
--m_current;
|
|
||||||
// Undo all events and states from the current time
|
// Undo all events and states from the current time
|
||||||
(*m_current)->undoAll();
|
(*m_current)->undo();
|
||||||
|
|
||||||
if ((*m_current)->getTicks() <= undo_ticks &&
|
if ( (*m_current)->getTicks() <= undo_ticks &&
|
||||||
(*m_current)->hasConfirmedState())
|
(*m_current)->isState() && (*m_current)->isConfirmed() )
|
||||||
{
|
{
|
||||||
return;
|
return (*m_current)->getTicks();
|
||||||
}
|
}
|
||||||
|
m_current--;
|
||||||
|
} while (m_current != m_all_rewind_info.end());
|
||||||
|
|
||||||
} // while m_current!=m_time_step_info.begin()
|
// Shouldn't happen
|
||||||
|
|
||||||
Log::error("RewindManager", "No state for rewind to %d",
|
Log::error("RewindManager", "No state for rewind to %d",
|
||||||
undo_ticks);
|
undo_ticks);
|
||||||
|
return -1;
|
||||||
} // undoUntil
|
} // undoUntil
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
/** Replays all events (not states) that happened at the specified time.
|
||||||
|
* \param ticks Time in ticks.
|
||||||
|
*/
|
||||||
|
void RewindQueue::replayAllEvents(int ticks)
|
||||||
|
{
|
||||||
|
// Replay all events that happened at the current time step
|
||||||
|
while ( m_current != m_all_rewind_info.end() &&
|
||||||
|
(*m_current)->getTicks() == ticks )
|
||||||
|
{
|
||||||
|
if ((*m_current)->isEvent())
|
||||||
|
(*m_current)->rewind();
|
||||||
|
m_current++;
|
||||||
|
if (!hasMoreRewindInfo()) break;
|
||||||
|
} // while current->getTIcks == ticks
|
||||||
|
|
||||||
|
} // replayAllEvents
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
/** Unit tests for RewindQueue. It tests:
|
/** Unit tests for RewindQueue. It tests:
|
||||||
* - Sorting order of RewindInfos at the same time (i.e. state before time
|
* - Sorting order of RewindInfos at the same time (i.e. state before time
|
||||||
@ -439,62 +361,74 @@ void RewindQueue::unitTesting()
|
|||||||
};
|
};
|
||||||
DummyRewinder *dummy_rewinder = new DummyRewinder();
|
DummyRewinder *dummy_rewinder = new DummyRewinder();
|
||||||
|
|
||||||
|
// First tests: add a state first, then an event, and make
|
||||||
|
// sure the state stays first
|
||||||
RewindQueue q0;
|
RewindQueue q0;
|
||||||
assert(q0.isEmpty());
|
assert(q0.isEmpty());
|
||||||
assert(!q0.hasMoreRewindInfo());
|
assert(!q0.hasMoreRewindInfo());
|
||||||
|
|
||||||
q0.addNewTimeStep(0, 0.5f);
|
q0.addLocalState(NULL, NULL, /*confirmed*/true, 0);
|
||||||
q0.m_current = q0.m_time_step_info.begin();
|
assert(q0.m_all_rewind_info.front()->isState());
|
||||||
assert(!q0.isEmpty());
|
assert(!q0.m_all_rewind_info.front()->isEvent());
|
||||||
assert(q0.hasMoreRewindInfo());
|
assert(q0.hasMoreRewindInfo());
|
||||||
assert(q0.m_time_step_info.size() == 1);
|
assert(q0.undoUntil(0) == 0);
|
||||||
|
|
||||||
assert((*q0.m_time_step_info.begin())->getNumberOfEvents() == 0);
|
|
||||||
q0.addLocalState(NULL, NULL, true, 0);
|
|
||||||
assert((*q0.m_time_step_info.begin())->getNumberOfEvents() == 1);
|
|
||||||
|
|
||||||
q0.addNewTimeStep(1, 0.5f);
|
|
||||||
assert(q0.m_time_step_info.size() == 2);
|
|
||||||
|
|
||||||
q0.addNetworkEvent(dummy_rewinder, NULL, 0);
|
q0.addNetworkEvent(dummy_rewinder, NULL, 0);
|
||||||
|
// Network events are not immediately merged
|
||||||
|
assert(q0.m_all_rewind_info.size() == 1);
|
||||||
|
|
||||||
bool needs_rewind;
|
bool needs_rewind;
|
||||||
int rewind_ticks;
|
int rewind_ticks;
|
||||||
int world_ticks = 0;
|
int world_ticks = 0;
|
||||||
float dt = 0.01f;
|
|
||||||
assert((*q0.m_time_step_info.begin())->getNumberOfEvents() == 1);
|
|
||||||
q0.mergeNetworkData(world_ticks, &needs_rewind, &rewind_ticks);
|
q0.mergeNetworkData(world_ticks, &needs_rewind, &rewind_ticks);
|
||||||
assert((*q0.m_time_step_info.begin())->getNumberOfEvents() == 2);
|
assert(q0.hasMoreRewindInfo());
|
||||||
|
assert(q0.m_all_rewind_info.size() == 2);
|
||||||
|
AllRewindInfo::iterator rii = q0.m_all_rewind_info.begin();
|
||||||
|
assert((*rii)->isState());
|
||||||
|
rii++;
|
||||||
|
assert((*rii)->isEvent());
|
||||||
|
|
||||||
// This will be added to timestep 0
|
// Another state must be sorted before the event:
|
||||||
q0.addNetworkEvent(dummy_rewinder, NULL, 2);
|
q0.addNetworkState(dummy_rewinder, NULL, 0);
|
||||||
dt = 0.01f; // to small, event from 0.2 will not be merged
|
assert(q0.hasMoreRewindInfo());
|
||||||
q0.mergeNetworkData(world_ticks, &needs_rewind, &rewind_ticks);
|
q0.mergeNetworkData(world_ticks, &needs_rewind, &rewind_ticks);
|
||||||
assert(q0.m_time_step_info.size() == 2);
|
assert(q0.m_all_rewind_info.size() == 3);
|
||||||
assert((*q0.m_time_step_info.begin())->getNumberOfEvents() == 2);
|
rii = q0.m_all_rewind_info.begin();
|
||||||
dt = 0.3f;
|
assert((*rii)->isState());
|
||||||
q0.mergeNetworkData(world_ticks, &needs_rewind, &rewind_ticks);
|
rii++;
|
||||||
assert(q0.m_time_step_info.size() == 2);
|
assert((*rii)->isState());
|
||||||
assert((*q0.m_time_step_info.begin())->getNumberOfEvents() == 3);
|
rii++;
|
||||||
|
assert((*rii)->isEvent());
|
||||||
|
|
||||||
// This event will get added to the last time step info at 1.0:
|
// Test time base comparisons: adding an event to the end
|
||||||
q0.addNetworkEvent(dummy_rewinder, NULL, 1);
|
q0.addLocalEvent(dummy_rewinder, NULL, true, 4);
|
||||||
world_ticks = 8;
|
// Then adding an earlier event
|
||||||
dt = 0.3f;
|
q0.addLocalEvent(dummy_rewinder, NULL, false, 1);
|
||||||
q0.mergeNetworkData(world_ticks, &needs_rewind, &rewind_ticks);
|
// rii points to the 3rd element, the ones added just now
|
||||||
// Note that end() is behind the list, i.e. invalid, but rbegin()
|
// should be elements4 and 5:
|
||||||
// is the last element
|
rii++;
|
||||||
assert((*q0.m_time_step_info.rbegin())->getNumberOfEvents() == 1);
|
assert((*rii)->getTicks()==1);
|
||||||
|
rii++;
|
||||||
|
assert((*rii)->getTicks()==4);
|
||||||
|
|
||||||
|
// Now test inserting an event first, then the state
|
||||||
|
RewindQueue q1;
|
||||||
|
q1.addLocalEvent(NULL, NULL, true, 5);
|
||||||
|
q1.addLocalState(NULL, NULL, true, 5);
|
||||||
|
rii = q1.m_all_rewind_info.begin();
|
||||||
|
assert((*rii)->isState());
|
||||||
|
rii++;
|
||||||
|
assert((*rii)->isEvent());
|
||||||
|
|
||||||
// Bugs seen before
|
// Bugs seen before
|
||||||
// ----------------
|
// ----------------
|
||||||
// 1) Current pointer was not reset from end of list when an event
|
// 1) Current pointer was not reset from end of list when an event
|
||||||
// was added and the pointer was already at end of list
|
// was added and the pointer was already at end of list
|
||||||
RewindQueue b1;
|
RewindQueue b1;
|
||||||
b1.addNewTimeStep(1, 0.1f);
|
b1.addLocalEvent(NULL, NULL, true, 1);
|
||||||
++b1; // Should now point at end of list
|
b1.next(); // Should now point at end of list
|
||||||
b1.hasMoreRewindInfo();
|
assert(!b1.hasMoreRewindInfo());
|
||||||
b1.addNewTimeStep(2, 0.1f);
|
b1.addLocalEvent(NULL, NULL, true, 2);
|
||||||
TimeStepInfo *tsi = b1.getCurrent();
|
RewindInfo *ri = b1.getCurrent();
|
||||||
assert(tsi->getTicks() == 2);
|
assert(ri->getTicks() == 2);
|
||||||
} // unitTesting
|
} // unitTesting
|
||||||
|
@ -37,11 +37,10 @@ class TimeStepInfo;
|
|||||||
class RewindQueue
|
class RewindQueue
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
/** Pointer to all saved */
|
|
||||||
typedef std::list<TimeStepInfo*> AllTimeStepInfo;
|
|
||||||
|
|
||||||
/** The list of all events that are affected by a rewind. */
|
typedef std::list<RewindInfo*> AllRewindInfo;
|
||||||
AllTimeStepInfo m_time_step_info;
|
|
||||||
|
AllRewindInfo m_all_rewind_info;
|
||||||
|
|
||||||
/** The list of all events received from the network. They are stored
|
/** The list of all events received from the network. They are stored
|
||||||
* in a separate thread (so this data structure is thread-save), and
|
* in a separate thread (so this data structure is thread-save), and
|
||||||
@ -51,29 +50,17 @@ private:
|
|||||||
typedef std::vector<RewindInfo*> AllNetworkRewindInfo;
|
typedef std::vector<RewindInfo*> AllNetworkRewindInfo;
|
||||||
Synchronised<AllNetworkRewindInfo> m_network_events;
|
Synchronised<AllNetworkRewindInfo> m_network_events;
|
||||||
|
|
||||||
/** Iterator to the curren time step info to be handled. This should
|
/** Iterator to the curren time step info to be handled. */
|
||||||
* always be at the same time as World::getTime(). */
|
AllRewindInfo::iterator m_current;
|
||||||
AllTimeStepInfo::iterator m_current;
|
|
||||||
|
|
||||||
AllTimeStepInfo::iterator findPreviousTimeStepInfo(int ticks);
|
|
||||||
void insertRewindInfo(RewindInfo *ri);
|
void insertRewindInfo(RewindInfo *ri);
|
||||||
|
|
||||||
struct _TimeStepInfoCompare
|
|
||||||
{
|
|
||||||
bool operator()(const TimeStepInfo * const ri1, const TimeStepInfo * const ri2) const;
|
|
||||||
} m_time_step_info_compare;
|
|
||||||
|
|
||||||
void testingSortingOrderType(EventRewinder *rewinder, int types[3]);
|
|
||||||
void testingSortingOrderTime(EventRewinder *rewinder, int types[3],
|
|
||||||
float times[3] );
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void unitTesting();
|
static void unitTesting();
|
||||||
|
|
||||||
RewindQueue();
|
RewindQueue();
|
||||||
~RewindQueue();
|
~RewindQueue();
|
||||||
void reset();
|
void reset();
|
||||||
void addNewTimeStep(int ticks, float dt);
|
|
||||||
void addLocalEvent(EventRewinder *event_rewinder, BareNetworkString *buffer,
|
void addLocalEvent(EventRewinder *event_rewinder, BareNetworkString *buffer,
|
||||||
bool confirmed, int ticks);
|
bool confirmed, int ticks);
|
||||||
void addLocalState(Rewinder *rewinder, BareNetworkString *buffer,
|
void addLocalState(Rewinder *rewinder, BareNetworkString *buffer,
|
||||||
@ -81,34 +68,30 @@ public:
|
|||||||
void addNetworkEvent(EventRewinder *event_rewinder,
|
void addNetworkEvent(EventRewinder *event_rewinder,
|
||||||
BareNetworkString *buffer, int ticks);
|
BareNetworkString *buffer, int ticks);
|
||||||
void addNetworkState(Rewinder *rewinder, BareNetworkString *buffer,
|
void addNetworkState(Rewinder *rewinder, BareNetworkString *buffer,
|
||||||
int ticks, float dt);
|
int ticks);
|
||||||
void mergeNetworkData(int world_ticks, bool *needs_rewind,
|
void mergeNetworkData(int world_ticks, bool *needs_rewind,
|
||||||
int *rewind_ticks);
|
int *rewind_ticks);
|
||||||
|
void replayAllEvents(int ticks);
|
||||||
bool isEmpty() const;
|
bool isEmpty() const;
|
||||||
bool hasMoreRewindInfo() const;
|
bool hasMoreRewindInfo() const;
|
||||||
void undoUntil(int undo_ticks);
|
int undoUntil(int undo_ticks);
|
||||||
float determineNextDT();
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
/** Returns the last (i.e. newest) entry in the TimeStepInfo list. This is
|
|
||||||
* used for rewinds, since it's the first TimeStep that must not be
|
|
||||||
* rewound. */
|
|
||||||
TimeStepInfo *getLast() { return *m_time_step_info.rbegin(); }
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
RewindQueue::AllTimeStepInfo::iterator& operator++()
|
/** Sets the current element to be the next one and returns the next
|
||||||
|
* RewindInfo element. */
|
||||||
|
void next()
|
||||||
{
|
{
|
||||||
assert(m_current != m_time_step_info.end());
|
assert(m_current != m_all_rewind_info.end());
|
||||||
m_current++;
|
m_current++;
|
||||||
return m_current;
|
return;
|
||||||
} // operator++
|
} // operator++
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
/** Returns the current RewindInfo. Caller must make sure that there is at least
|
/** Returns the current RewindInfo. Caller must make sure that there is at
|
||||||
* one more RewindInfo (see hasMoreRewindInfo()). */
|
* least one more RewindInfo (see hasMoreRewindInfo()). */
|
||||||
TimeStepInfo *getCurrent()
|
RewindInfo* getCurrent()
|
||||||
{
|
{
|
||||||
assert(m_current != m_time_step_info.end());
|
return (m_current != m_all_rewind_info.end() ) ? *m_current : NULL;
|
||||||
return *m_current;
|
|
||||||
} // getNext
|
} // getNext
|
||||||
|
|
||||||
}; // RewindQueue
|
}; // RewindQueue
|
||||||
|
@ -1,101 +0,0 @@
|
|||||||
//
|
|
||||||
// SuperTuxKart - a fun racing game with go-kart
|
|
||||||
// Copyright (C) 2013 Joerg Henrichs
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or
|
|
||||||
// modify it under the terms of the GNU General Public License
|
|
||||||
// as published by the Free Software Foundation; either version 3
|
|
||||||
// of the License, or (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program; if not, write to the Free Software
|
|
||||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
#include "network/time_step_info.hpp"
|
|
||||||
|
|
||||||
#include "network/rewind_info.hpp"
|
|
||||||
#include "physics/physics.hpp"
|
|
||||||
|
|
||||||
/** Creates a new TimeStepInfo for a given time and given dt.
|
|
||||||
* \param time Time for this TimeStepInfo object.
|
|
||||||
* \param dt Time step size.
|
|
||||||
*/
|
|
||||||
TimeStepInfo::TimeStepInfo(int ticks, float dt)
|
|
||||||
{
|
|
||||||
m_ticks = ticks;
|
|
||||||
m_dt = dt;
|
|
||||||
// In case of unit testing physics does not exist
|
|
||||||
if (Physics::getInstance())
|
|
||||||
m_local_physics_time = Physics::getInstance()->getPhysicsWorld()
|
|
||||||
->getLocalTime();
|
|
||||||
else
|
|
||||||
m_local_physics_time = 0.0f;
|
|
||||||
} // StateEventList
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------
|
|
||||||
/** Adds a state. State must be saved first, i.e. before events. The
|
|
||||||
* RewindManager guarantees this order
|
|
||||||
* \param ri The RewindInfo object for the state.
|
|
||||||
*/
|
|
||||||
void TimeStepInfo::insert(RewindInfo *ri)
|
|
||||||
{
|
|
||||||
if (ri->isState())
|
|
||||||
{
|
|
||||||
// States need to be inserted first.
|
|
||||||
// FIXME: handle duplicated states, e.g. server doing a rewind
|
|
||||||
// and sending another updated state
|
|
||||||
AllRewindInfo::iterator i = m_list_of_events.begin();
|
|
||||||
while (i != m_list_of_events.end() && (*i)->isState())
|
|
||||||
++i;
|
|
||||||
m_list_of_events.insert(i, ri);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Events at the same time are just added to the end
|
|
||||||
m_list_of_events.push_back(ri);
|
|
||||||
}
|
|
||||||
} // insert
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------
|
|
||||||
/** Undos all events and states for this time step.
|
|
||||||
*/
|
|
||||||
void TimeStepInfo::undoAll()
|
|
||||||
{
|
|
||||||
AllRewindInfo::reverse_iterator i;
|
|
||||||
for (i = m_list_of_events.rbegin(); i != m_list_of_events.rend(); i++)
|
|
||||||
{
|
|
||||||
(*i)->undo();
|
|
||||||
}
|
|
||||||
} // undoAll
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------
|
|
||||||
/** Replays all events for this TimeStepInfo.
|
|
||||||
*/
|
|
||||||
void TimeStepInfo::replayAllEvents()
|
|
||||||
{
|
|
||||||
AllRewindInfo::reverse_iterator i;
|
|
||||||
for (i = m_list_of_events.rbegin(); i != m_list_of_events.rend(); i++)
|
|
||||||
{
|
|
||||||
if ((*i)->isEvent())
|
|
||||||
(*i)->rewind();
|
|
||||||
}
|
|
||||||
} // replayAllEvents
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------
|
|
||||||
/** Replays all state information for this TimeStepInfo.
|
|
||||||
*/
|
|
||||||
void TimeStepInfo::replayAllStates()
|
|
||||||
{
|
|
||||||
AllRewindInfo::reverse_iterator i;
|
|
||||||
for (i = m_list_of_events.rbegin(); i != m_list_of_events.rend(); i++)
|
|
||||||
{
|
|
||||||
if ((*i)->isState())
|
|
||||||
(*i)->rewind();
|
|
||||||
}
|
|
||||||
} // replayAllStates
|
|
||||||
|
|
@ -1,102 +0,0 @@
|
|||||||
//
|
|
||||||
// SuperTuxKart - a fun racing game with go-kart
|
|
||||||
// Copyright (C) 2017 Joerg Henrichs
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or
|
|
||||||
// modify it under the terms of the GNU General Public License
|
|
||||||
// as published by the Free Software Foundation; either version 3
|
|
||||||
// of the License, or (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program; if not, write to the Free Software
|
|
||||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
#ifndef HEADER_TIME_STEP_INFO_HPP
|
|
||||||
#define HEADER_TIME_STEP_INFO_HPP
|
|
||||||
|
|
||||||
#include "network/rewind_info.hpp"
|
|
||||||
#include <assert.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
class RewindInfo;
|
|
||||||
class EventRewinder;
|
|
||||||
|
|
||||||
/** \ingroup network
|
|
||||||
*/
|
|
||||||
|
|
||||||
class RewindInfo;
|
|
||||||
class RewindInfoEvent;
|
|
||||||
class RewindInfoState;
|
|
||||||
|
|
||||||
/** This class stores information about each time step on a client or server.
|
|
||||||
* Firstly it stores the world time and time step size. In case of a rewind
|
|
||||||
* this allows the rewind to use the same time step size, which reduces
|
|
||||||
* jitter caused by different time step size. Secondly for each time it
|
|
||||||
* stores (if exist) all states, and all events. The TimeStepInfo acts as a
|
|
||||||
* container and will store all states and events that happened 'around' time,
|
|
||||||
* i.e. between time-X and time+Y (X and Y are implicitely defined in the
|
|
||||||
* RewindQueue). This avoids that messages from clients to the server create
|
|
||||||
* more and more TimeStepInfo, with smaller and smaller dt, which would make
|
|
||||||
* rewinds more expensive.
|
|
||||||
*/
|
|
||||||
class TimeStepInfo
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
typedef std::vector<RewindInfo*> AllRewindInfo;
|
|
||||||
|
|
||||||
/** The list of all states and events at a certain time. */
|
|
||||||
AllRewindInfo m_list_of_events;
|
|
||||||
|
|
||||||
/** Time at which those events should be executed here. */
|
|
||||||
int m_ticks;
|
|
||||||
|
|
||||||
/** Time step to be used. */
|
|
||||||
float m_dt;
|
|
||||||
|
|
||||||
/** Bullet maintains a 'left over' time since it is running with a fixed
|
|
||||||
* 60 fps. Restoring this value exactly improves accuracy of rewinds. */
|
|
||||||
float m_local_physics_time;
|
|
||||||
public:
|
|
||||||
TimeStepInfo(int ticks, float dt);
|
|
||||||
void insert(RewindInfo *ri);
|
|
||||||
void undoAll();
|
|
||||||
void replayAllEvents();
|
|
||||||
void replayAllStates();
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
/** Sets the tiem of this object. */
|
|
||||||
void setTicks(int ticks) { m_ticks = ticks; }
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
/** Sets the time step size of this object. */
|
|
||||||
void setDT(float dt) { m_dt = dt; }
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
/** Returns the time for this TimeStepInfo instance. */
|
|
||||||
int getTicks() const { return m_ticks; }
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
/** Returns the left-over physics time. */
|
|
||||||
float getLocalPhysicsTime() const { return m_local_physics_time; }
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
/** Returns the (previous) time step size, so that rewindw can be done
|
|
||||||
* with same time step size. */
|
|
||||||
float getDT() const { return m_dt; }
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
/** Returns if this TimeStepInfo instance has a confirmed state, i.e. if
|
|
||||||
* a rewind can start from this time. */
|
|
||||||
bool hasConfirmedState() const
|
|
||||||
{
|
|
||||||
if (m_list_of_events.empty()) return false;
|
|
||||||
const RewindInfo *ri = m_list_of_events[0];
|
|
||||||
return ri->isState() && ri->isConfirmed();
|
|
||||||
} // hasConfirmedState
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
/** Returns the number of events (and states) at this time step. Used
|
|
||||||
* in unit testing. */
|
|
||||||
int getNumberOfEvents() const { return m_list_of_events.size(); }
|
|
||||||
}; // TimeStepInfo
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user