Started to work on rewinding. ATM a single kart can be rewound
to a previous time (i.e. the race continuous from the previous location). No memory handling is done, ... all work in progress :) git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/rewind@13468 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
parent
c787926e04
commit
9fe8d0f259
@ -97,8 +97,8 @@ src/items/projectile_manager.cpp
|
||||
src/items/rubber_ball.cpp
|
||||
src/items/rubber_band.cpp
|
||||
src/items/swatter.cpp
|
||||
src/karts/abstract_kart_animation.cpp
|
||||
src/karts/abstract_kart.cpp
|
||||
src/karts/abstract_kart_animation.cpp
|
||||
src/karts/cannon_animation.cpp
|
||||
src/karts/controller/ai_base_controller.cpp
|
||||
src/karts/controller/ai_properties.cpp
|
||||
@ -113,6 +113,7 @@ src/karts/kart_gfx.cpp
|
||||
src/karts/kart_model.cpp
|
||||
src/karts/kart_properties.cpp
|
||||
src/karts/kart_properties_manager.cpp
|
||||
src/karts/kart_rewinder.cpp
|
||||
src/karts/kart_with_stats.cpp
|
||||
src/karts/max_speed.cpp
|
||||
src/karts/moveable.cpp
|
||||
@ -144,6 +145,8 @@ src/network/network_manager.cpp
|
||||
src/network/race_info_message.cpp
|
||||
src/network/race_result_message.cpp
|
||||
src/network/race_state.cpp
|
||||
src/network/rewind_manager.cpp
|
||||
src/network/rewinder.cpp
|
||||
src/physics/btKart.cpp
|
||||
src/physics/btKartRaycast.cpp
|
||||
src/physics/btUprightConstraint.cpp
|
||||
@ -187,13 +190,13 @@ src/states_screens/help_screen_4.cpp
|
||||
src/states_screens/kart_selection.cpp
|
||||
src/states_screens/main_menu_screen.cpp
|
||||
src/states_screens/options_screen_audio.cpp
|
||||
src/states_screens/options_screen_input2.cpp
|
||||
src/states_screens/options_screen_input.cpp
|
||||
src/states_screens/options_screen_input2.cpp
|
||||
src/states_screens/options_screen_players.cpp
|
||||
src/states_screens/options_screen_ui.cpp
|
||||
src/states_screens/options_screen_video.cpp
|
||||
src/states_screens/race_gui_base.cpp
|
||||
src/states_screens/race_gui.cpp
|
||||
src/states_screens/race_gui_base.cpp
|
||||
src/states_screens/race_gui_overworld.cpp
|
||||
src/states_screens/race_result_gui.cpp
|
||||
src/states_screens/race_setup_screen.cpp
|
||||
@ -253,8 +256,8 @@ src/animations/animation_base.hpp
|
||||
src/animations/ipo.hpp
|
||||
src/animations/three_d_animation.hpp
|
||||
src/audio/dummy_sfx.hpp
|
||||
src/audio/music_dummy.hpp
|
||||
src/audio/music.hpp
|
||||
src/audio/music_dummy.hpp
|
||||
src/audio/music_information.hpp
|
||||
src/audio/music_manager.hpp
|
||||
src/audio/music_ogg.hpp
|
||||
@ -262,8 +265,8 @@ src/audio/sfx_base.hpp
|
||||
src/audio/sfx_buffer.hpp
|
||||
src/audio/sfx_manager.hpp
|
||||
src/audio/sfx_openal.hpp
|
||||
src/challenges/challenge_data.hpp
|
||||
src/challenges/challenge.hpp
|
||||
src/challenges/challenge_data.hpp
|
||||
src/challenges/game_slot.hpp
|
||||
src/challenges/unlock_manager.hpp
|
||||
src/config/device_config.hpp
|
||||
@ -305,11 +308,11 @@ src/guiengine/scalable_font.hpp
|
||||
src/guiengine/screen.hpp
|
||||
src/guiengine/skin.hpp
|
||||
src/guiengine/widget.hpp
|
||||
src/guiengine/widgets.hpp
|
||||
src/guiengine/widgets/bubble_widget.hpp
|
||||
src/guiengine/widgets/button_widget.hpp
|
||||
src/guiengine/widgets/check_box_widget.hpp
|
||||
src/guiengine/widgets/dynamic_ribbon_widget.hpp
|
||||
src/guiengine/widgets.hpp
|
||||
src/guiengine/widgets/icon_button_widget.hpp
|
||||
src/guiengine/widgets/label_widget.hpp
|
||||
src/guiengine/widgets/list_widget.hpp
|
||||
@ -321,8 +324,8 @@ src/guiengine/widgets/spinner_widget.hpp
|
||||
src/guiengine/widgets/text_box_widget.hpp
|
||||
src/input/binding.hpp
|
||||
src/input/device_manager.hpp
|
||||
src/input/input_device.hpp
|
||||
src/input/input.hpp
|
||||
src/input/input_device.hpp
|
||||
src/input/input_manager.hpp
|
||||
src/input/wiimote.hpp
|
||||
src/input/wiimote_manager.hpp
|
||||
@ -344,8 +347,8 @@ src/items/projectile_manager.hpp
|
||||
src/items/rubber_ball.hpp
|
||||
src/items/rubber_band.hpp
|
||||
src/items/swatter.hpp
|
||||
src/karts/abstract_kart_animation.hpp
|
||||
src/karts/abstract_kart.hpp
|
||||
src/karts/abstract_kart_animation.hpp
|
||||
src/karts/cannon_animation.hpp
|
||||
src/karts/controller/ai_base_controller.hpp
|
||||
src/karts/controller/ai_properties.hpp
|
||||
@ -356,11 +359,12 @@ src/karts/controller/player_controller.hpp
|
||||
src/karts/controller/skidding_ai.hpp
|
||||
src/karts/explosion_animation.hpp
|
||||
src/karts/ghost_kart.hpp
|
||||
src/karts/kart_gfx.hpp
|
||||
src/karts/kart.hpp
|
||||
src/karts/kart_gfx.hpp
|
||||
src/karts/kart_model.hpp
|
||||
src/karts/kart_properties.hpp
|
||||
src/karts/kart_properties_manager.hpp
|
||||
src/karts/kart_rewinder.hpp
|
||||
src/karts/kart_with_stats.hpp
|
||||
src/karts/max_speed.hpp
|
||||
src/karts/moveable.hpp
|
||||
@ -400,6 +404,8 @@ src/network/race_result_message.hpp
|
||||
src/network/race_start_message.hpp
|
||||
src/network/race_state.hpp
|
||||
src/network/remote_kart_info.hpp
|
||||
src/network/rewind_manager.hpp
|
||||
src/network/rewinder.hpp
|
||||
src/network/world_loaded_message.hpp
|
||||
src/physics/btKart.hpp
|
||||
src/physics/btKartRaycast.hpp
|
||||
@ -447,13 +453,13 @@ src/states_screens/help_screen_4.hpp
|
||||
src/states_screens/kart_selection.hpp
|
||||
src/states_screens/main_menu_screen.hpp
|
||||
src/states_screens/options_screen_audio.hpp
|
||||
src/states_screens/options_screen_input2.hpp
|
||||
src/states_screens/options_screen_input.hpp
|
||||
src/states_screens/options_screen_input2.hpp
|
||||
src/states_screens/options_screen_players.hpp
|
||||
src/states_screens/options_screen_ui.hpp
|
||||
src/states_screens/options_screen_video.hpp
|
||||
src/states_screens/race_gui_base.hpp
|
||||
src/states_screens/race_gui.hpp
|
||||
src/states_screens/race_gui_base.hpp
|
||||
src/states_screens/race_gui_overworld.hpp
|
||||
src/states_screens/race_result_gui.hpp
|
||||
src/states_screens/race_setup_screen.hpp
|
||||
@ -483,8 +489,8 @@ src/tracks/check_sphere.hpp
|
||||
src/tracks/check_structure.hpp
|
||||
src/tracks/graph_node.hpp
|
||||
src/tracks/lod_node_loader.hpp
|
||||
src/tracks/quad_graph.hpp
|
||||
src/tracks/quad.hpp
|
||||
src/tracks/quad_graph.hpp
|
||||
src/tracks/quad_set.hpp
|
||||
src/tracks/terrain_info.hpp
|
||||
src/tracks/track.hpp
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "modes/demo_world.hpp"
|
||||
#include "modes/profile_world.hpp"
|
||||
#include "modes/world.hpp"
|
||||
#include "network/rewind_manager.hpp"
|
||||
#include "physics/physics.hpp"
|
||||
#include "race/history.hpp"
|
||||
#include "replay/replay_recorder.hpp"
|
||||
@ -92,6 +93,8 @@ InputManager::~InputManager()
|
||||
void InputManager::handleStaticAction(int key, int value)
|
||||
{
|
||||
static bool control_is_pressed = false;
|
||||
static bool shift_is_pressed = false;
|
||||
|
||||
World *world = World::getWorld();
|
||||
|
||||
// When no players... a cutscene
|
||||
@ -131,7 +134,10 @@ void InputManager::handleStaticAction(int key, int value)
|
||||
case KEY_LWIN:
|
||||
control_is_pressed = value!=0;
|
||||
break;
|
||||
|
||||
case KEY_LSHIFT:
|
||||
case KEY_RSHIFT:
|
||||
case KEY_SHIFT:
|
||||
shift_is_pressed = value!=0; break;
|
||||
case KEY_KEY_I:
|
||||
{
|
||||
if (!world || !UserConfigParams::m_artist_debug_mode) break;
|
||||
@ -161,7 +167,18 @@ void InputManager::handleStaticAction(int key, int value)
|
||||
break;
|
||||
|
||||
case KEY_F1:
|
||||
if (UserConfigParams::m_artist_debug_mode && world)
|
||||
if(value && shift_is_pressed && world && RewindManager::isEnabled())
|
||||
{
|
||||
printf("Enter rewind time:");
|
||||
char s[256];
|
||||
fgets(s, 256, stdin);
|
||||
float t;
|
||||
StringUtils::fromString(s,t);
|
||||
RewindManager::get()->rewindTo(world->getTime()-t);
|
||||
Log::info("Rewind", "Rewinding from %f to %f",
|
||||
world->getTime(), world->getTime()-t);
|
||||
}
|
||||
else if (UserConfigParams::m_artist_debug_mode && world)
|
||||
{
|
||||
AbstractKart* kart = world->getLocalPlayerKart(0);
|
||||
|
||||
|
@ -422,6 +422,8 @@ void RubberBall::moveTowardsTarget(Vec3 *next_xyz, float dt)
|
||||
// at it directly, stop interpolating, instead fly straight
|
||||
// towards it.
|
||||
Vec3 diff = m_target->getXYZ()-getXYZ();
|
||||
if(diff.length()==0)
|
||||
printf("diff=0\n");
|
||||
*next_xyz = getXYZ() + (dt*m_speed/diff.length())*diff;
|
||||
|
||||
Vec3 old_vec = getXYZ()-m_previous_xyz;
|
||||
|
@ -21,6 +21,8 @@
|
||||
|
||||
#include "network/message.hpp"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* \ingroup controller
|
||||
*/
|
||||
@ -73,6 +75,21 @@ public:
|
||||
m_fire = false;
|
||||
m_look_back = false;
|
||||
} // reset
|
||||
// ------------------------------------------------------------------------
|
||||
/** Tests if two KartControls are equal.
|
||||
*/
|
||||
bool operator==(const KartControl &other)
|
||||
{
|
||||
return m_steer == other.m_steer &&
|
||||
m_accel == other.m_accel &&
|
||||
m_brake == other.m_brake &&
|
||||
m_nitro == other.m_nitro &&
|
||||
m_skid == other.m_skid &&
|
||||
m_rescue == other.m_rescue &&
|
||||
m_fire == other.m_fire &&
|
||||
m_look_back == other.m_look_back;
|
||||
} // operator==
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Return the serialised size in bytes. */
|
||||
static int getLength() { return 9; }
|
||||
@ -84,6 +101,24 @@ public:
|
||||
m->addFloat(m_accel);
|
||||
m->addChar(getButtonsCompressed());
|
||||
} // compress
|
||||
// ------------------------------------------------------------------------
|
||||
/** Copies the important data from this objects into a memory buffer. */
|
||||
void copyToMemory(char *buffer)
|
||||
{
|
||||
memcpy(buffer, &m_steer, sizeof(float));
|
||||
memcpy(buffer+sizeof(float), &m_accel, sizeof(float));
|
||||
buffer[2*sizeof(float)] = getButtonsCompressed();
|
||||
} // copyToMemory
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Restores this object from a previously saved memory buffer. */
|
||||
void setFromMemory(char *buffer)
|
||||
{
|
||||
memcpy(&m_steer, buffer, sizeof(float));
|
||||
memcpy(&m_accel, buffer+4*sizeof(float), sizeof(float));
|
||||
setButtonsCompressed(buffer[2*sizeof(float)]);
|
||||
} // setFromMemory
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
void uncompress(char *c)
|
||||
{
|
||||
|
@ -19,13 +19,6 @@
|
||||
|
||||
#include "karts/kart.hpp"
|
||||
|
||||
#include <math.h>
|
||||
#include <iostream>
|
||||
#include <algorithm> // for min and max
|
||||
|
||||
#include <ICameraSceneNode.h>
|
||||
#include <ISceneManager.h>
|
||||
|
||||
#include "audio/music_manager.hpp"
|
||||
#include "audio/sfx_manager.hpp"
|
||||
#include "audio/sfx_base.hpp"
|
||||
@ -44,6 +37,7 @@
|
||||
#include "guiengine/scalable_font.hpp"
|
||||
#include "karts/explosion_animation.hpp"
|
||||
#include "karts/kart_gfx.hpp"
|
||||
#include "karts/kart_rewinder.hpp"
|
||||
#include "karts/rescue_animation.hpp"
|
||||
#include "modes/overworld.hpp"
|
||||
#include "modes/world.hpp"
|
||||
@ -71,6 +65,13 @@
|
||||
#include "utils/log.hpp" //TODO: remove after debugging is done
|
||||
|
||||
|
||||
#include <math.h>
|
||||
#include <iostream>
|
||||
#include <algorithm> // for min and max
|
||||
|
||||
#include <ICameraSceneNode.h>
|
||||
#include <ISceneManager.h>
|
||||
|
||||
|
||||
#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__)
|
||||
// Disable warning for using 'this' in base member initializer list
|
||||
@ -220,6 +221,8 @@ void Kart::init(RaceManager::KartType type)
|
||||
0.0f) );
|
||||
|
||||
reset();
|
||||
|
||||
m_rewinder = new KartRewinder(this);
|
||||
} // init
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -229,6 +232,8 @@ void Kart::init(RaceManager::KartType type)
|
||||
*/
|
||||
Kart::~Kart()
|
||||
{
|
||||
delete m_rewinder;
|
||||
|
||||
// Delete all custom sounds (TODO: add back when properly done)
|
||||
/*
|
||||
for (int n = 0; n < SFXManager::NUM_CUSTOMS; n++)
|
||||
@ -1055,6 +1060,8 @@ void Kart::eliminate()
|
||||
*/
|
||||
void Kart::update(float dt)
|
||||
{
|
||||
m_rewinder->update();
|
||||
|
||||
if ( UserConfigParams::m_graphical_effects )
|
||||
{
|
||||
// update star effect (call will do nothing if stars are not activated)
|
||||
|
@ -34,15 +34,16 @@
|
||||
#include "tracks/terrain_info.hpp"
|
||||
#include "utils/no_copy.hpp"
|
||||
|
||||
|
||||
class AbstractKartAnimation;
|
||||
class Attachment;
|
||||
class btKart;
|
||||
class btUprightConstraint;
|
||||
|
||||
class Attachment;
|
||||
class Controller;
|
||||
class Item;
|
||||
class AbstractKartAnimation;
|
||||
class HitEffect;
|
||||
class Item;
|
||||
class KartGFX;
|
||||
class KartRewinder;
|
||||
class MaxSpeed;
|
||||
class ParticleEmitter;
|
||||
class ParticleKind;
|
||||
@ -198,6 +199,9 @@ private:
|
||||
float m_view_blocked_by_plunger;
|
||||
float m_speed;
|
||||
|
||||
/** The rewinder object for network play. */
|
||||
KartRewinder *m_rewinder;
|
||||
|
||||
std::vector<SFXBase*> m_custom_sounds;
|
||||
SFXBase *m_beep_sound;
|
||||
SFXBase *m_engine_sound;
|
||||
|
96
src/karts/kart_rewinder.cpp
Normal file
96
src/karts/kart_rewinder.cpp
Normal file
@ -0,0 +1,96 @@
|
||||
//
|
||||
// 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 "karts/kart_rewinder.hpp"
|
||||
#include "karts/abstract_kart.hpp"
|
||||
#include "modes/world.hpp"
|
||||
#include "network/rewind_manager.hpp"
|
||||
#include "utils/vec3.hpp"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
KartRewinder::KartRewinder(AbstractKart *kart) : Rewinder(/*can_be_destroyed*/ false)
|
||||
{
|
||||
m_kart = kart;
|
||||
} // KartRewinder
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Saves all state information for a kart in a memory buffer. The memory
|
||||
* is allocated here and the address returned. It will then be managed
|
||||
* by the RewindManager. The size is used to keep track of memory usage
|
||||
* for rewinding.
|
||||
* \param[out] buffer Address of the memory buffer.
|
||||
* \returns Size of allocated memory, or -1 in case of an error.
|
||||
*/
|
||||
int KartRewinder::getState(char **buffer) const
|
||||
{
|
||||
const int MEMSIZE = 13*sizeof(float);
|
||||
|
||||
*buffer = new char[MEMSIZE];
|
||||
float* p = (float*)*buffer;
|
||||
if(!buffer)
|
||||
{
|
||||
Log::error("KartRewinder", "Can not allocate %d bytes.", MEMSIZE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const btRigidBody *body = m_kart->getBody();
|
||||
|
||||
const btTransform &t = body->getWorldTransform();
|
||||
btQuaternion q = t.getRotation();
|
||||
memcpy(p+ 0, t.getOrigin(), 3*sizeof(float));
|
||||
memcpy(p+ 3, &q, 4*sizeof(float));
|
||||
memcpy(p+ 7, body->getLinearVelocity(), 3*sizeof(float));
|
||||
memcpy(p+10, body->getAngularVelocity(), 3*sizeof(float));
|
||||
return MEMSIZE;
|
||||
} // getState
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Called once a frame. It will add a new kart control event to the rewind
|
||||
* manager if any control values have changed.
|
||||
*/
|
||||
void KartRewinder::update()
|
||||
{
|
||||
if(m_kart->getControls() == m_previous_control)
|
||||
return;
|
||||
m_previous_control = m_kart->getControls();
|
||||
|
||||
char *buffer = new char[m_previous_control.getLength()];
|
||||
m_previous_control.copyToMemory(buffer);
|
||||
|
||||
// The rewind manager will free the memory once it's not needed anymore
|
||||
RewindManager::get()->addEvent(this, World::getWorld()->getTime(), buffer);
|
||||
} // update
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Actuall rewind to the specified state. */
|
||||
void KartRewinder::rewindToState(char *buffer)
|
||||
{
|
||||
btTransform t;
|
||||
float *p = (float*)buffer;
|
||||
t.setOrigin(*(btVector3*)p);
|
||||
t.setRotation(*(btQuaternion*)(p+3));
|
||||
btRigidBody *body = m_kart->getBody();
|
||||
body->proceedToTransform(t);
|
||||
body->setLinearVelocity(*(btVector3*)(p+7));
|
||||
body->setAngularVelocity(*(btVector3*)(p+10));
|
||||
|
||||
return;
|
||||
} // rewindToState
|
||||
|
||||
|
63
src/karts/kart_rewinder.hpp
Normal file
63
src/karts/kart_rewinder.hpp
Normal file
@ -0,0 +1,63 @@
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef HEADER_KART_REWINDER_HPP
|
||||
#define HEADER_KART_REWINDER_HPP
|
||||
|
||||
#include "karts/controller/kart_control.hpp"
|
||||
|
||||
#include "network/rewinder.hpp"
|
||||
#include "utils/cpp2011.h"
|
||||
|
||||
class AbstractKart;
|
||||
|
||||
class KartRewinder : public Rewinder
|
||||
{
|
||||
private:
|
||||
/** Pointer to the original kart object. */
|
||||
AbstractKart *m_kart;
|
||||
|
||||
KartControl m_previous_control;
|
||||
|
||||
public:
|
||||
KartRewinder(AbstractKart *kart);
|
||||
virtual ~KartRewinder() {};
|
||||
virtual int getState(char **buffer) const;
|
||||
virtual void rewindToState(char *p) OVERRIDE;
|
||||
virtual void rewindToEvent(char *p) OVERRIDE
|
||||
{
|
||||
}; // rewindToEvent
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
virtual void undoState(char *p) OVERRIDE
|
||||
{
|
||||
}; // undoState
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
virtual void undoEvent(char *p) OVERRIDE
|
||||
{
|
||||
}; // undoEvent
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
void update();
|
||||
|
||||
|
||||
}; // Rewinder
|
||||
#endif
|
||||
|
@ -168,6 +168,7 @@
|
||||
#include "modes/demo_world.hpp"
|
||||
#include "modes/profile_world.hpp"
|
||||
#include "network/network_manager.hpp"
|
||||
#include "network/rewind_manager.hpp"
|
||||
#include "race/grand_prix_manager.hpp"
|
||||
#include "race/highscore_manager.hpp"
|
||||
#include "race/history.hpp"
|
||||
@ -590,7 +591,7 @@ int handleCmdLinePreliminary(int argc, char **argv)
|
||||
} // --verbose or -v
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // handleCmdLinePreliminary
|
||||
|
||||
// ============================================================================
|
||||
/** Handles command line options.
|
||||
@ -638,6 +639,10 @@ int handleCmdLine(int argc, char **argv)
|
||||
{
|
||||
UserConfigParams::m_camera_debug=1;
|
||||
}
|
||||
else if( !strcmp(argv[i], "--rewind") )
|
||||
{
|
||||
RewindManager::setEnable(true);
|
||||
}
|
||||
else if(UserConfigParams::m_artist_debug_mode &&
|
||||
!strcmp(argv[i], "--physics-debug"))
|
||||
{
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include "modes/profile_world.hpp"
|
||||
#include "network/network_manager.hpp"
|
||||
#include "network/race_state.hpp"
|
||||
#include "network/rewind_manager.hpp"
|
||||
#include "physics/btKart.hpp"
|
||||
#include "physics/physics.hpp"
|
||||
#include "physics/triangle_mesh.hpp"
|
||||
@ -148,6 +149,8 @@ void World::init()
|
||||
// constructor is called, so the wrong race gui would be created.
|
||||
createRaceGUI();
|
||||
|
||||
RewindManager::create();
|
||||
|
||||
// Grab the track file
|
||||
m_track = track_manager->getTrack(race_manager->getTrackName());
|
||||
if(!m_track)
|
||||
@ -201,6 +204,8 @@ void World::init()
|
||||
*/
|
||||
void World::reset()
|
||||
{
|
||||
RewindManager::get()->reset();
|
||||
|
||||
// If m_saved_race_gui is set, it means that the restart was done
|
||||
// when the race result gui was being shown. In this case restore the
|
||||
// race gui (note that the race result gui is cached and so never really
|
||||
@ -346,6 +351,8 @@ Controller* World::loadAIController(AbstractKart *kart)
|
||||
//-----------------------------------------------------------------------------
|
||||
World::~World()
|
||||
{
|
||||
RewindManager::destroy();
|
||||
|
||||
if(ReplayPlay::get())
|
||||
{
|
||||
// Destroy the old replay object, which also stored the ghost
|
||||
@ -844,7 +851,9 @@ void World::update(float dt)
|
||||
|
||||
projectile_manager->update(dt);
|
||||
|
||||
PROFILER_POP_CPU_MARKER();
|
||||
RewindManager::get()->update(dt);
|
||||
|
||||
PROFILER_POP_CPU_MARKER();
|
||||
|
||||
#ifdef DEBUG
|
||||
assert(m_magic_number == 0xB01D6543);
|
||||
|
333
src/network/rewind_manager.cpp
Normal file
333
src/network/rewind_manager.cpp
Normal file
@ -0,0 +1,333 @@
|
||||
//
|
||||
// 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/rewind_manager.hpp"
|
||||
|
||||
#include "modes/world.hpp"
|
||||
#include "network/rewinder.hpp"
|
||||
#include "utils/log.hpp"
|
||||
|
||||
RewindManager* RewindManager::m_rewind_manager = NULL;
|
||||
bool RewindManager::m_enable_rewind_manager = false;
|
||||
|
||||
/** Creates the singleton. */
|
||||
RewindManager *RewindManager::create()
|
||||
{
|
||||
assert(!m_rewind_manager);
|
||||
m_rewind_manager = new RewindManager();
|
||||
return m_rewind_manager;
|
||||
} // create
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Destroys the singleton. */
|
||||
void RewindManager::destroy()
|
||||
{
|
||||
assert(m_rewind_manager);
|
||||
delete m_rewind_manager;
|
||||
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, float time,
|
||||
char *buffer, bool is_event,
|
||||
bool is_confirmed)
|
||||
{
|
||||
m_rewinder = rewinder;
|
||||
m_time = time;
|
||||
m_buffer = buffer;
|
||||
m_is_event = is_event;
|
||||
m_is_confirmed = is_confirmed;
|
||||
} // RewindInfo
|
||||
|
||||
// ============================================================================
|
||||
/** The constructor.
|
||||
*/
|
||||
RewindManager::RewindManager()
|
||||
{
|
||||
reset();
|
||||
} // RewindManager
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Frees all saved state information. Note that the Rewinder data must be
|
||||
* freed elsewhere.
|
||||
*/
|
||||
RewindManager::~RewindManager()
|
||||
{
|
||||
// Destroying the
|
||||
for(unsigned int i=0; i<m_rewind_info.size(); i++)
|
||||
{
|
||||
delete m_rewind_info[i];
|
||||
}
|
||||
m_rewind_info.clear();
|
||||
} // ~RewindManager
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Frees all saved state information and all destroyable rewinder.
|
||||
*/
|
||||
void RewindManager::reset()
|
||||
{
|
||||
#ifdef REWIND_SEARCH_STATS
|
||||
m_count_of_comparisons = 0;
|
||||
m_count_of_searches = 0;
|
||||
#endif
|
||||
m_overall_state_size = 0;
|
||||
if(!m_enable_rewind_manager) return;
|
||||
|
||||
AllRewinder::iterator r = m_all_rewinder.begin();
|
||||
while(r!=m_all_rewinder.end())
|
||||
{
|
||||
if(!(*r)->canBeDestroyed())
|
||||
{
|
||||
r++;
|
||||
continue;
|
||||
}
|
||||
Rewinder *rewinder = *r;
|
||||
r = m_all_rewinder.erase(r);
|
||||
// FIXME Do we really want to delete this here?
|
||||
delete rewinder;
|
||||
}
|
||||
|
||||
for(unsigned int i=0; i<m_rewind_info.size(); i++)
|
||||
{
|
||||
delete m_rewind_info[i];
|
||||
}
|
||||
m_rewind_info.clear();
|
||||
} // reset
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void RewindManager::insertRewindInfo(RewindInfo *ri)
|
||||
{
|
||||
#ifdef REWIND_SEARCH_STATS
|
||||
m_count_of_searches++;
|
||||
#endif
|
||||
float t = ri->getTime();
|
||||
|
||||
if(ri->isEvent())
|
||||
{
|
||||
// If there are several infos for the same time t,
|
||||
// events must be inserted at the end
|
||||
AllRewindInfo::reverse_iterator i = m_rewind_info.rbegin();
|
||||
while(i!=m_rewind_info.rend() &&
|
||||
(*i)->getTime() > t)
|
||||
{
|
||||
#ifdef REWIND_SEARCH_STATS
|
||||
m_count_of_comparisons++;
|
||||
#endif
|
||||
i++;
|
||||
}
|
||||
AllRewindInfo::iterator insert_point = i.base();
|
||||
m_rewind_info.insert(insert_point,ri);
|
||||
return;
|
||||
|
||||
}
|
||||
else // is a states
|
||||
{
|
||||
// If there are several infos for the same time t,
|
||||
// a state must be inserted first
|
||||
AllRewindInfo::reverse_iterator i = m_rewind_info.rbegin();
|
||||
while(i!=m_rewind_info.rend() && (*i)->getTime() >= t)
|
||||
{
|
||||
#ifdef REWIND_SEARCH_STATS
|
||||
m_count_of_comparisons++;
|
||||
#endif
|
||||
i++;
|
||||
}
|
||||
AllRewindInfo::iterator insert_point = i.base();
|
||||
m_rewind_info.insert(insert_point,ri);
|
||||
return;
|
||||
}
|
||||
} // insertRewindData
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Returns the first (i.e. lowest) index i in m_rewind_info which fulfills
|
||||
* time(i) < target_time <= time(i+1)
|
||||
* This is used to determine the starting point from which to rewind.
|
||||
* \param time Time for which an index is searched.
|
||||
* \return Index in m_rewind_info after which to add rewind data.
|
||||
*/
|
||||
unsigned int RewindManager::findFirstIndex(float target_time) const
|
||||
{
|
||||
// For now do a linear search, even though m_rewind_info is sorted
|
||||
// I would expect that most insertions will be towards the (very)
|
||||
// end of the list. Note that after finding an entry in a binary
|
||||
// search, you stil have to do a linear search to find the last
|
||||
// entry with the same time in order to minimise the later
|
||||
// necessary memory move.
|
||||
|
||||
// Gather some statistics about search for now:
|
||||
#ifdef REWIND_SEARCH_STATS
|
||||
m_count_of_searches++;
|
||||
#endif
|
||||
int index = m_rewind_info.size()-1;
|
||||
while(index>=0)
|
||||
{
|
||||
#ifdef REWIND_SEARCH_STATS
|
||||
m_count_of_comparisons++;
|
||||
#endif
|
||||
if(m_rewind_info[index]->getTime()<target_time)
|
||||
return index;
|
||||
index--;
|
||||
}
|
||||
|
||||
// For now just exit here
|
||||
Log::fatal("RewindManager",
|
||||
"Inserting before first state at %f, insert at %f.",
|
||||
m_rewind_info[0]->getTime(), target_time);
|
||||
return 0; // avoid compiler warning
|
||||
} // findFirstIndex
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Adds an event to the rewind data. The data to be stored 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.
|
||||
*/
|
||||
void RewindManager::addEvent(Rewinder *rewinder, float time, char *buffer)
|
||||
{
|
||||
RewindInfo *ri = new RewindInfo(rewinder, time, buffer, /*is_event*/true,
|
||||
/*is_confirmed*/true);
|
||||
insertRewindInfo(ri);
|
||||
} // addEvent
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Determines if a new state snapshot should be taken, and if so calls all
|
||||
* rewinder to do so.
|
||||
* \param dt Time step size.
|
||||
*/
|
||||
void RewindManager::update(float dt)
|
||||
{
|
||||
if(!m_enable_rewind_manager || m_all_rewinder.size()==0) return;
|
||||
|
||||
float time = World::getWorld()->getTime();
|
||||
|
||||
// For now always create a snapshot.
|
||||
for(unsigned int i=0; i<m_all_rewinder.size(); i++)
|
||||
{
|
||||
char *p;
|
||||
int size = m_all_rewinder[i]->getState(&p);
|
||||
if(size>=0)
|
||||
{
|
||||
m_overall_state_size += size;
|
||||
RewindInfo *ri = new RewindInfo(m_all_rewinder[i], time, p,
|
||||
/*is_event*/false,
|
||||
/*is_confirmed*/true);
|
||||
assert(ri);
|
||||
insertRewindInfo(ri);
|
||||
} // size >= 0
|
||||
}
|
||||
|
||||
Log::verbose("RewindManager", "%f allocated %ld bytes search %d/%d=%f",
|
||||
World::getWorld()->getTime(), m_overall_state_size,
|
||||
m_count_of_comparisons, m_count_of_searches,
|
||||
float(m_count_of_comparisons)/ float(m_count_of_searches) );
|
||||
|
||||
} // update
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Rewinds to the specified time.
|
||||
* \param t Time to rewind to.
|
||||
*/
|
||||
void RewindManager::rewindTo(float rewind_time)
|
||||
{
|
||||
// First find the state to which we need to rewind
|
||||
// ------------------------------------------------
|
||||
int state = findFirstIndex(rewind_time);
|
||||
|
||||
if(m_rewind_info[state]->isEvent())
|
||||
{
|
||||
Log::error("RewindManager", "No state for rewind to %d, state %d.",
|
||||
rewind_time, state);
|
||||
return;
|
||||
}
|
||||
|
||||
// Then undo the states that are skipped
|
||||
// -------------------------------------
|
||||
for(int i=m_rewind_info.size()-1; i>(int)state; i--)
|
||||
{
|
||||
m_rewind_info[i]->undo();
|
||||
} // for i>state
|
||||
|
||||
|
||||
// TODO: we need some logic here to handle that confirmed states are not
|
||||
// necessarily the same for each rewinder. So if we rewind to t, one
|
||||
// rewinder forces us to go back to t1 (latest state saved before t),
|
||||
// another one to t2.
|
||||
// So we have to detect the latest time t_min < t for which each
|
||||
// rewinder has a confirmed state. Then we rewind from t_min. But if we
|
||||
// find another confirmed state for a rewinder at time t1 (t_min<t1<t),
|
||||
// we have to overwrite the current state of the rewinder with that at
|
||||
// time t1.
|
||||
// Also care needs to be taken with events: e.g. either we make steering
|
||||
// information (for kars) part of the state, or we have to go even further
|
||||
// back in time (before t_min) to find what state steering was in at time
|
||||
// t_min.
|
||||
|
||||
// Rewind to the required state
|
||||
// ----------------------------
|
||||
float current_time = m_rewind_info[state]->getTime();
|
||||
|
||||
// Rewind all objects (and also all state) that happen at the
|
||||
// current time.
|
||||
// TODO: this assumes atm that all rewinder have a initial state
|
||||
// for 'current_time'!!!
|
||||
while(m_rewind_info[state]->getTime()==current_time)
|
||||
{
|
||||
m_rewind_info[state]->rewind();
|
||||
state ++;
|
||||
} // while rewind info at time current_time
|
||||
|
||||
// Store the time to which we have to replay to
|
||||
float current_time = World::getWorld()->getTime();
|
||||
|
||||
World::setTime(current_time);
|
||||
|
||||
// Now go forward through the saved states, and
|
||||
// replay taking the events into account.
|
||||
while(World::getWorld()->getTime() < current_time)
|
||||
{
|
||||
|
||||
// Find the next event which needs to be taken into account
|
||||
// All other states can be deleted
|
||||
// TODO ... for now
|
||||
while(state < m_rewind_info.size() && !m_rewind_info[state]->isEvent())
|
||||
state ++;
|
||||
float dt = determineTimeStepSize(state);
|
||||
World::getWorld()->update(dt);
|
||||
|
||||
}
|
||||
|
||||
} // rewindTo
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** 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 state The next state to replay.
|
||||
* \return The time step size to use in the next simulation step.
|
||||
*/
|
||||
float RewindManager::determineTimeStepSize(int state)
|
||||
{
|
||||
float dt = 1.0f/60.0f;
|
||||
float t = World::getWorld()->getTime();
|
||||
if(m_rewind_info[state]->getTime() < t + dt)
|
||||
return t-m_rewind_info[state]->getTime();
|
||||
return dt;
|
||||
} // determineTimeStepSize
|
195
src/network/rewind_manager.hpp
Normal file
195
src/network/rewind_manager.hpp
Normal file
@ -0,0 +1,195 @@
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef HEADER_REWIND_MANAGER_HPP
|
||||
#define HEADER_REWIND_MANAGER_HPP
|
||||
|
||||
#include "network/rewinder.hpp"
|
||||
#include "utils/ptr_vector.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
#include <vector>
|
||||
|
||||
|
||||
class RewindManager
|
||||
{
|
||||
private:
|
||||
/** Singleton pointer. */
|
||||
static RewindManager *m_rewind_manager;
|
||||
|
||||
/** En- or Disable the rewind manager. This is used to disable storing
|
||||
* rewind data in case of local races only. */
|
||||
static bool m_enable_rewind_manager;
|
||||
|
||||
typedef std::vector<Rewinder *> AllRewinder;
|
||||
|
||||
/** A list of all objects that can be rewound. */
|
||||
AllRewinder m_all_rewinder;
|
||||
|
||||
/** Overall amount of memory allocated by states. */
|
||||
unsigned int m_overall_state_size;
|
||||
|
||||
// ========================================================================
|
||||
/** 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:
|
||||
/** Pointer to the buffer which stores all states. */
|
||||
char *m_buffer;
|
||||
|
||||
/** Time when this state was taken. */
|
||||
float m_time;
|
||||
|
||||
/** True if this is an event, and not a state. */
|
||||
bool m_is_event;
|
||||
|
||||
/** 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, float time, char *buffer,
|
||||
bool is_event, bool is_confirmed);
|
||||
// --------------------------------------------------------------------
|
||||
~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; }
|
||||
// --------------------------------------------------------------------
|
||||
bool isEvent() const { return m_is_event; }
|
||||
// --------------------------------------------------------------------
|
||||
/** Returns if this state is confirmed. */
|
||||
bool isConfirmed() const { return m_is_confirmed; }
|
||||
// --------------------------------------------------------------------
|
||||
/** Called when going back in time to undo any rewind information.
|
||||
* It calls either undoEvent or undoState in the rewinder. */
|
||||
void undo()
|
||||
{
|
||||
if(m_is_event)
|
||||
m_rewinder->undoEvent(m_buffer);
|
||||
else
|
||||
m_rewinder->undoState(m_buffer);
|
||||
} // 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_is_event)
|
||||
m_rewinder->rewindToEvent(m_buffer);
|
||||
else
|
||||
{
|
||||
if(m_is_confirmed)
|
||||
m_rewinder->rewindToState(m_buffer);
|
||||
else
|
||||
{
|
||||
// TODO
|
||||
// Handle replacing of stored states.
|
||||
}
|
||||
}
|
||||
} // rewind
|
||||
}; // RewindInfo
|
||||
// ========================================================================
|
||||
|
||||
/** Pointer to all saved states. */
|
||||
typedef std::vector<RewindInfo*> AllRewindInfo;
|
||||
|
||||
AllRewindInfo m_rewind_info;
|
||||
|
||||
#define REWIND_SEARCH_STATS
|
||||
|
||||
#ifdef REWIND_SEARCH_STATS
|
||||
/** Gather some statistics about how many comparisons we do,
|
||||
* to find out if it's worth doing a binary search.*/
|
||||
mutable int m_count_of_comparisons;
|
||||
mutable int m_count_of_searches;
|
||||
#endif
|
||||
|
||||
RewindManager();
|
||||
~RewindManager();
|
||||
unsigned int findFirstIndex(float time) const;
|
||||
void insertRewindInfo(RewindInfo *ri);
|
||||
|
||||
public:
|
||||
// First static functions to manage rewinding.
|
||||
// ===========================================
|
||||
static RewindManager *create();
|
||||
static void destroy();
|
||||
// ------------------------------------------------------------------------
|
||||
/** En- or disables rewinding. */
|
||||
static void setEnable(bool m) { m_enable_rewind_manager = m;}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns if rewinding is enabled or not. */
|
||||
static bool isEnabled() { return m_enable_rewind_manager; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the singleton. This function will not automatically create
|
||||
* the singleton. */
|
||||
static RewindManager *get()
|
||||
{
|
||||
assert(m_rewind_manager);
|
||||
return m_rewind_manager;
|
||||
} // get
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
void reset();
|
||||
void update(float dt);
|
||||
// ------------------------------------------------------------------------
|
||||
/** Adds a Rewinder to the list of all rewinders.
|
||||
* \return true If rewinding is enabled, false otherwise.
|
||||
*/
|
||||
bool addRewinder(Rewinder *rewinder)
|
||||
{
|
||||
if(!m_enable_rewind_manager) return false;
|
||||
m_all_rewinder.push_back(rewinder);
|
||||
return true;
|
||||
} // addRewinder
|
||||
// ------------------------------------------------------------------------
|
||||
void rewindTo(float target_time);
|
||||
// ------------------------------------------------------------------------
|
||||
void addEvent(Rewinder *rewinder, float time, char *buffer);
|
||||
}; // RewindManager
|
||||
|
||||
|
||||
#endif
|
||||
|
37
src/network/rewinder.cpp
Normal file
37
src/network/rewinder.cpp
Normal file
@ -0,0 +1,37 @@
|
||||
//
|
||||
// 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/rewinder.hpp"
|
||||
|
||||
#include "network/rewind_manager.hpp"
|
||||
|
||||
/** Constructor. It will add this object to the list of all rewindable
|
||||
* objects in the rewind manager.
|
||||
*/
|
||||
Rewinder::Rewinder(bool can_be_destroyed)
|
||||
{
|
||||
m_can_be_destroyed = can_be_destroyed;
|
||||
RewindManager::get()->addRewinder(this);
|
||||
} // Rewinder
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Destructor.
|
||||
*/
|
||||
Rewinder::~Rewinder()
|
||||
{
|
||||
} // ~Rewinder
|
68
src/network/rewinder.hpp
Normal file
68
src/network/rewinder.hpp
Normal file
@ -0,0 +1,68 @@
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef HEADER_REWINDER_HPP
|
||||
#define HEADER_REWINDER_HPP
|
||||
|
||||
class Rewinder
|
||||
{
|
||||
private:
|
||||
bool m_can_be_destroyed;
|
||||
public:
|
||||
Rewinder(bool can_be_destroyed);
|
||||
virtual ~Rewinder();
|
||||
|
||||
/** Provides a copy of the state of the object in one memory buffer.
|
||||
* The memory is managed by the RewindManager.
|
||||
* \param[out] buffer The address of the memory buffer with the state.
|
||||
* \return Size of the buffer, or -1 in case of an error.
|
||||
*/
|
||||
virtual int getState(char **buffer) const = 0;
|
||||
|
||||
/** Called when an event needs to be undone. This is called while going
|
||||
* backwards for rewinding - all stored events will get an 'undo' call.
|
||||
* A dummy implementation is provided which just ignores this.
|
||||
*/
|
||||
virtual void undoEvent(char *buffer) = 0;
|
||||
|
||||
/** Called when an event needs to be replayed. This is called during
|
||||
* rewind, i.e. when going forward in time again.
|
||||
*/
|
||||
virtual void rewindToEvent(char *buffer) = 0;
|
||||
|
||||
/** Called when a state needs to be replayed. This is called during
|
||||
* rewind, i.e. when going forward in time again, and only for confirmed
|
||||
* states.
|
||||
*/
|
||||
virtual void rewindToState(char *buffer) = 0;
|
||||
|
||||
/** Undo the effects of the given state, but do not rewind to that
|
||||
* state (which is done by rewindTo). This is called while going
|
||||
* backwards for rewinding - all stored events will get an 'undo' call.
|
||||
* Provided here a dummy implementation that just ignores the state.
|
||||
*/
|
||||
virtual void undoState(char *p) = 0;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
/** True if this rewinder can be destroyed. Karts can not be destroyed,
|
||||
* cakes can. This is used by the RewindManager in reset. */
|
||||
bool canBeDestroyed() const { return m_can_be_destroyed; }
|
||||
|
||||
}; // Rewinder
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user