Refactored RewindInfo into astand-alone class/file with separate

classes for time, event, and states.
This commit is contained in:
hiker 2016-08-03 17:48:38 +10:00
parent 3eb94e023d
commit 1025e25846
5 changed files with 291 additions and 160 deletions

View File

@ -0,0 +1,55 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2016 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/rewind_info.hpp"
#include "modes/world.hpp"
#include "physics/physics.hpp"
/** Constructor for a state: it only takes the size, and allocates a buffer
* for all state info.
* \param size Necessary buffer size for a state.
*/
RewindInfo::RewindInfo(float time, bool is_confirmed)
{
m_time = time;
m_is_confirmed = is_confirmed;
} // RewindInfo
// ============================================================================
RewindInfoTime::RewindInfoTime(float time)
: RewindInfo(time, /*is_confirmed*/true)
{
} // RewindInfoTime
// ============================================================================
RewindInfoState::RewindInfoState(float time, Rewinder *rewinder, char *buffer,
bool is_confirmed)
: RewindInfoRewinder(time, rewinder, buffer, is_confirmed)
{
m_local_physics_time = World::getWorld()->getPhysics()->getPhysicsWorld()
->getLocalTime();
} // RewindInfoState
// ============================================================================
RewindInfoEvent::RewindInfoEvent(float time, Rewinder *rewinder, char *buffer,
bool is_confirmed)
: RewindInfoRewinder(time, rewinder, buffer, is_confirmed)
{
} // RewindInfoEvent

199
src/network/rewind_info.hpp Normal file
View File

@ -0,0 +1,199 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2016 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_REWIND_INFO_HPP
#define HEADER_REWIND_INFO_HPP
#include "network/rewinder.hpp"
#include "utils/ptr_vector.hpp"
#include <assert.h>
#include <vector>
/** Used to store rewind information for a given time for all rewind
* instances.
* Rewind information can either be a state (for example a kart would
* have position, rotation, linear and angular velocity, ... as state),
* or an event (for a kart that would be pressing or releasing of a key).
* State changes and events can be delivered in different frequencies,
* and might be released (to save memory) differently: A state can be
* reproduced from a previous state by replaying the simulation taking
* all events into account.
*/
class RewindInfo
{
private:
/** Time when this state was taken. */
float m_time;
/** A confirmed event is one that was sent from the server. When
* rewinding we have to start with a confirmed state for each
* object. */
bool m_is_confirmed;
public:
RewindInfo(float time, bool is_confirmed);
/** Called when going back in time to undo any rewind information. */
virtual void undo() = 0;
/** This is called while going forwards in time again to reach current
* time. */
virtual void rewind() = 0;
// ------------------------------------------------------------------------
virtual ~RewindInfo() { }
// ------------------------------------------------------------------------
/** Returns the time at which this rewind state was saved. */
float getTime() const { return m_time; }
// ------------------------------------------------------------------------
/** Returns if this state is confirmed. */
bool isConfirmed() const { return m_is_confirmed; }
// ------------------------------------------------------------------------
/** If this rewind info is an event. Subclasses will overwrite this. */
virtual bool isEvent() const { return false; }
// ------------------------------------------------------------------------
/** If this rewind info is time info. Subclasses will overwrite this. */
virtual bool isTime() const { return false; }
// ------------------------------------------------------------------------
/** If this rewind info is an event. Subclasses will overwrite this. */
virtual bool isState() const { return false; }
// ------------------------------------------------------------------------
}; // RewindInfo
// ============================================================================
/** A rewind info abstract class that keeps track of a rewinder object.
*/
class RewindInfoRewinder : public RewindInfo
{
protected:
/** The Rewinder instance for which this data is. */
Rewinder *m_rewinder;
/** Pointer to the buffer which stores all states. */
char *m_buffer;
public:
RewindInfoRewinder(float time, Rewinder *rewinder, char *buffer,
bool is_confirmed)
: RewindInfo(time, is_confirmed)
{
m_rewinder = rewinder;
m_buffer = buffer;
} // RewindInfoRewinder
// ------------------------------------------------------------------------
~RewindInfoRewinder()
{
} // ~RewindInfoRewinder
}; // RewindInfoRewinder
// ============================================================================
class RewindInfoTime : public RewindInfo
{
private:
public:
RewindInfoTime(float time);
virtual ~RewindInfoTime() {};
// ------------------------------------------------------------------------
virtual bool isTime() const { return true; }
// ------------------------------------------------------------------------
/** Called when going back in time to undo any rewind information.
* Does actually nothing. */
virtual void undo() {}
// ------------------------------------------------------------------------
/** Rewinds to this state. Nothing to be done for time info. */
virtual void rewind() {}
}; // class RewindInfoTime
// ============================================================================
class RewindInfoState: public RewindInfoRewinder
{
private:
/** The 'left over' time from the physics. */
float m_local_physics_time;
public:
RewindInfoState(float time, Rewinder *rewinder, char *buffer,
bool is_confirmed);
virtual ~RewindInfoState() {};
// ------------------------------------------------------------------------
/** Returns the left-over physics time. */
float getLocalPhysicsTime() const { return m_local_physics_time; }
// ------------------------------------------------------------------------
/** Returns a pointer to the state buffer. */
char *getBuffer() const { return m_buffer; }
// ------------------------------------------------------------------------
virtual bool isState() const { return true; }
// ------------------------------------------------------------------------
/** Called when going back in time to undo any rewind information.
* It calls undoState in the rewinder. */
virtual void undo()
{
m_rewinder->undoState(m_buffer);
} // undoEvent
// ------------------------------------------------------------------------
/** Rewinds to this state. This is called while going forwards in time
* again to reach current time. It will call rewindToState(char *)
* if the state is a confirmed state. */
virtual void rewind()
{
if (isConfirmed())
m_rewinder->rewindToState(m_buffer);
else
{
// TODO
// Handle replacing of stored states.
}
} // rewind
}; // class RewindInfoState
// ============================================================================
class RewindInfoEvent : public RewindInfoRewinder
{
public:
RewindInfoEvent(float time, Rewinder *rewinder, char *buffer,
bool is_confirmed);
virtual ~RewindInfoEvent() {}
// ------------------------------------------------------------------------
/** Returns a pointer to the state buffer. */
char *getBuffer() const { return m_buffer; }
// ------------------------------------------------------------------------
virtual bool isEvent() const { return true; }
// ------------------------------------------------------------------------
/** Called when going back in time to undo any rewind information.
* It calls undoEvent in the rewinder. */
virtual void undo()
{
m_rewinder->undoEvent(m_buffer);
} // undo
// ------------------------------------------------------------------------
/** This is called while going forwards in time again to reach current
* time. Calls rewindEvent(char*).
*/
virtual void rewind()
{
m_rewinder->rewindToEvent(m_buffer);
} // rewind
}; // class RewindIndoEvent
#endif

View File

@ -21,6 +21,7 @@
#include "graphics/irr_driver.hpp"
#include "modes/world.hpp"
#include "network/rewinder.hpp"
#include "network/rewind_info.hpp"
#include "physics/physics.hpp"
#include "race/history.hpp"
#include "utils/log.hpp"
@ -45,35 +46,6 @@ void RewindManager::destroy()
m_rewind_manager = NULL;
} // destroy
// ============================================================================
/** Constructor for a state: it only takes the size, and allocates a buffer
* for all state info.
* \param size Necessary buffer size for a state.
*/
RewindManager::RewindInfo::RewindInfo(Rewinder *rewinder, char *buffer,
bool is_event, bool is_confirmed)
{
m_rewinder = rewinder;
m_time = RewindManager::get()->getCurrentTime();
m_time_step = RewindManager::get()->getCurrentTimeStep();
m_local_physics_time = World::getWorld()->getPhysics()->getPhysicsWorld()->getLocalTime();
m_buffer = buffer;
m_type = is_event ? RIT_EVENT : RIT_STATE;
m_is_confirmed = is_confirmed;
} // RewindInfo
// ----------------------------------------------------------------------------
RewindManager::RewindInfo::RewindInfo()
{
m_rewinder = NULL;
m_time = RewindManager::get()->getCurrentTime();
m_time_step = RewindManager::get()->getCurrentTimeStep();
m_local_physics_time = World::getWorld()->getPhysics()->getPhysicsWorld()->getLocalTime();
m_buffer = NULL;
m_type = RIT_TIME;
m_is_confirmed = true;
} // RewindInfo
// ============================================================================
/** The constructor.
*/
@ -239,8 +211,8 @@ unsigned int RewindManager::findFirstIndex(float target_time) const
void RewindManager::addEvent(Rewinder *rewinder, char *buffer)
{
if(m_is_rewinding) return;
RewindInfo *ri = new RewindInfo(rewinder, buffer, /*is_event*/true,
/*is_confirmed*/true);
RewindInfo *ri = new RewindInfoEvent(getCurrentTime(), rewinder,
buffer, /*is_confirmed*/true );
insertRewindInfo(ri);
} // addEvent
@ -260,7 +232,7 @@ void RewindManager::saveStates()
{
// No full state necessary, add a dummy entry for the time
// which increases replay precision (same time step size)
RewindInfo *ri = new RewindInfo();
RewindInfo *ri = new RewindInfoTime(getCurrentTime());
insertRewindInfo(ri);
return;
}
@ -273,9 +245,9 @@ void RewindManager::saveStates()
if(size>=0)
{
m_overall_state_size += size;
RewindInfo *ri = new RewindInfo(m_all_rewinder[i], p,
/*is_event*/false,
/*is_confirmed*/true);
RewindInfo *ri = new RewindInfoState(getCurrentTime(),
m_all_rewinder[i], p,
/*is_confirmed*/true);
assert(ri);
insertRewindInfo(ri);
} // size >= 0
@ -302,18 +274,18 @@ void RewindManager::rewindTo(float rewind_time)
// First find the state to which we need to rewind
// ------------------------------------------------
int state = findFirstIndex(rewind_time);
int state_index = findFirstIndex(rewind_time);
if(!m_rewind_info[state]->isState())
if(!m_rewind_info[state_index]->isState())
{
Log::error("RewindManager", "No state for rewind to %f, state %d.",
rewind_time, state);
rewind_time, state_index);
return;
}
// Then undo the states that are skipped
// -------------------------------------
for(int i=m_rewind_info.size()-1; i>(int)state; i--)
for(int i=m_rewind_info.size()-1; i>(int)state_index; i--)
{
m_rewind_info[i]->undo();
} // for i>state
@ -335,8 +307,11 @@ void RewindManager::rewindTo(float rewind_time)
// Rewind to the required state
// ----------------------------
float exact_rewind_time = m_rewind_info[state]->getTime();
float local_physics_time = m_rewind_info[state]->getLocalPhysicsTime();
RewindInfoState *state =
dynamic_cast<RewindInfoState*>(m_rewind_info[state_index]);
float exact_rewind_time = state->getTime();
float local_physics_time = state->getLocalPhysicsTime();
World *world = World::getWorld();
world->getPhysics()->getPhysicsWorld()->setLocalTime(local_physics_time);
@ -353,22 +328,21 @@ void RewindManager::rewindTo(float rewind_time)
// Now go forward through the saved states, and
// replay taking the events into account.
while( world->getTime() < current_time &&
state < (int)m_rewind_info.size() )
state_index < (int)m_rewind_info.size() )
{
float dt = determineTimeStepSize(state, current_time);
// Now set all events and confirmed states
while(state < (int)m_rewind_info.size() &&
m_rewind_info[state]->getTime()<=world->getTime()+0.001f)
while(state_index < (int)m_rewind_info.size() &&
m_rewind_info[state_index]->getTime()<=world->getTime()+0.001f)
{
if(m_rewind_info[state]->isEvent() ||
m_rewind_info[state]->isConfirmed() )
if(m_rewind_info[state_index]->isEvent() ||
m_rewind_info[state_index]->isConfirmed() )
{
m_rewind_info[state]->rewind();
m_rewind_info[state_index]->rewind();
}
state++;
state_index++;
}
float dt = determineTimeStepSize(state_index, current_time);
world->updateWorld(dt);
#define SHOW_ROLLBACK
#ifdef SHOW_ROLLBACK
@ -391,9 +365,17 @@ void RewindManager::rewindTo(float rewind_time)
*/
float RewindManager::determineTimeStepSize(int next_state, float end_time)
{
return m_rewind_info[next_state]->getTimeStep();
// If there is a next state (which is known to have a different time)
// use the time difference to determine the time step size.
if(next_state < m_rewind_info.size())
return m_rewind_info[next_state]->getTime() - World::getWorld()->getTime();
// Otherwise, i.e. we are rewinding the last state/event, take the
// difference between that time and the world time at which the rewind
// was triggered.
return end_time - m_rewind_info[next_state-1]->getTime();
return m_rewind_info[next_state]->getTime()-World::getWorld()->getTime();
float dt = 1.0f/60.0f;
float t = World::getWorld()->getTime();

View File

@ -25,6 +25,8 @@
#include <assert.h>
#include <vector>
class RewindInfo;
/** \ingroup network
* This class manages rewinding. It keeps track of:
* - states for each rewindable object (for example a kart would have
@ -85,113 +87,6 @@ private:
/** A list of all objects that can be rewound. */
AllRewinder m_all_rewinder;
// ========================================================================
/** Used to store rewind information for a given time for all rewind
* instances.
* Rewind information can either be a state (for example a kart would
* have position, rotation, linear and angular velocity, ... as state),
* or an event (for a kart that would be pressing or releasing of a key).
* State changes and events can be delivered in different frequencies,
* and might be released (to save memory) differently: A state can be
* reproduced from a previous state by replaying the simulation taking
* all events into account.
*/
class RewindInfo
{
private:
/** The different information types that can be saved. */
enum RewindInfoType {RIT_TIME, RIT_STATE, RIT_EVENT};
/** Pointer to the buffer which stores all states. */
char *m_buffer;
/** Time when this state was taken. */
float m_time;
/** Time step size. */
float m_time_step;
/** The 'left over' time from the physics. */
float m_local_physics_time;
/** Type of this information. */
RewindInfoType m_type;
/** A confirmed event is one that was sent from the server. When
* rewinding we have to start with a confirmed state for each
* object. */
bool m_is_confirmed;
/** The Rewinder instance for which this data is. */
Rewinder *m_rewinder;
public:
RewindInfo(Rewinder *rewinder, char *buffer,
bool is_event, bool is_confirmed);
// --------------------------------------------------------------------
RewindInfo();
// --------------------------------------------------------------------
~RewindInfo()
{
delete m_buffer;
} // ~RewindInfo
// --------------------------------------------------------------------
/** Returns a pointer to the state buffer. */
char *getBuffer() const { return m_buffer; }
// --------------------------------------------------------------------
/** Returns the time at which this rewind state was saved. */
float getTime() const { return m_time; }
// --------------------------------------------------------------------
/** Time step size. */
float getTimeStep() const { return m_time_step; }
// --------------------------------------------------------------------
bool isEvent() const { return m_type==RIT_EVENT; }
// --------------------------------------------------------------------
bool isTime() const { return m_type==RIT_TIME; }
// --------------------------------------------------------------------
bool isState() const { return m_type==RIT_STATE; }
// --------------------------------------------------------------------
/** Returns if this state is confirmed. */
bool isConfirmed() const { return m_is_confirmed; }
// --------------------------------------------------------------------
/** Returns the left-over physics time. */
float getLocalPhysicsTime() const { return m_local_physics_time; }
// --------------------------------------------------------------------
/** Called when going back in time to undo any rewind information.
* It calls either undoEvent or undoState in the rewinder. */
void undo()
{
if(m_type==RIT_EVENT)
m_rewinder->undoEvent(m_buffer);
else if(m_type==RIT_STATE)
m_rewinder->undoState(m_buffer);
// time evnet can be ignored.
} // undoEvent
// --------------------------------------------------------------------
/** Rewinds to this state. This is called while going forwards in time
* again to reach current time. If the info is a state, it will
* call rewindToState(char *) if the state is a confirmed state, or
* rewindReplace(char*) in order to discard the old stored data,
* and replace it with the new state at that time. In case of an
* event, rewindEvent(char*) is called.
*/
void rewind()
{
if(m_type==RIT_EVENT)
m_rewinder->rewindToEvent(m_buffer);
else if(m_type==RIT_STATE)
{
if(m_is_confirmed)
m_rewinder->rewindToState(m_buffer);
else
{
// TODO
// Handle replacing of stored states.
}
} // time information can be ignored
} // rewind
}; // RewindInfo
// ========================================================================
/** Pointer to all saved states. */
typedef std::vector<RewindInfo*> AllRewindInfo;