diff --git a/sources.cmake b/sources.cmake index f484b15d5..dfc0e3774 100644 --- a/sources.cmake +++ b/sources.cmake @@ -1,5 +1,5 @@ # 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_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp") file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*") diff --git a/src/network/rewind_info.cpp b/src/network/rewind_info.cpp new file mode 100644 index 000000000..87d361c66 --- /dev/null +++ b/src/network/rewind_info.cpp @@ -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 + diff --git a/src/network/rewind_info.hpp b/src/network/rewind_info.hpp new file mode 100644 index 000000000..ecdc2d66b --- /dev/null +++ b/src/network/rewind_info.hpp @@ -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 +#include + + /** 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 diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index f3312f3ca..501b755e6 100644 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -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(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(); diff --git a/src/network/rewind_manager.hpp b/src/network/rewind_manager.hpp index 6b747bbd9..3d93c3709 100644 --- a/src/network/rewind_manager.hpp +++ b/src/network/rewind_manager.hpp @@ -25,6 +25,8 @@ #include #include +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 AllRewindInfo;