Removed the storing of DT (which is not necessary when using ticks

now). Removed TimeStep information class etc.
This commit is contained in:
hiker 2018-03-30 18:32:48 +11:00
parent 17b3a79997
commit 2192d1383f
13 changed files with 234 additions and 564 deletions

View File

@ -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/*")

View File

@ -300,7 +300,7 @@ void InputManager::handleStaticAction(int key, int value)
fgets(s, 256, stdin);
int t;
StringUtils::fromString(s,t);
RewindManager::get()->rewindTo(t);
RewindManager::get()->rewindTo(t, world->getTimeTicks());
Log::info("Rewind", "Rewinding from %d to %d",
world->getTimeTicks(), t);
}

View File

@ -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
* trigger a state change in the specified variable (without actually
* doing it). If it will trigger a state change, the marco will trigger
* immediatley a return to the caller. If dry_run is false, it will only
* doing it). If it will trigger a state change, the macro will
* 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
* early). The do-while(0) helps using this macro e.g. in the 'then'
* clause of an if statement. */
@ -125,7 +125,7 @@ bool PlayerController::action(PlayerAction action, int value, bool dry_run)
} while(0)
/** 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'(). */
#define SET_OR_TEST_GETTER(name, value) \
do \

View File

@ -154,9 +154,8 @@ float MainLoop::getLimitedDt()
dt *= 0.001f;
// If this is a client, the server might request an
// adjustment of this client's world clock (to reduce
// number of rewinds).
// If this is a client, the server might request an adjustment of
// this client's world clock (to reduce number of rewinds).
if (World::getWorld() &&
NetworkConfig::get()->isClient() &&
!RewindManager::get()->isRewinding() )
@ -297,14 +296,6 @@ void MainLoop::run()
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)
{
float frame_duration = num_steps * dt;
@ -338,16 +329,6 @@ void MainLoop::run()
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
m_is_last_substep = (i == num_steps - 1);

View File

@ -139,6 +139,7 @@ void GameProtocol::controllerAction(int kart_id, PlayerAction action,
BareNetworkString *s = new BareNetworkString(4);
s->addUInt8(kart_id).addUInt8(action).addUInt32(value)
.addUInt32(val_l).addUInt32(val_r);
RewindManager::get()->addEvent(this, s, /*confirmed*/true,
World::getWorld()->getTimeTicks() );
@ -255,7 +256,9 @@ void GameProtocol::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)
{
@ -263,6 +266,7 @@ void GameProtocol::addState(BareNetworkString *buffer)
m_data_to_send->addUInt16(buffer->size());
(*m_data_to_send) += *buffer;
} // addState
// ----------------------------------------------------------------------------
/** Called when the last state information has been added and the message
* can be sent to the clients.

View File

@ -48,10 +48,6 @@ RewindInfoState::RewindInfoState(int ticks, Rewinder *rewinder,
BareNetworkString *buffer, bool 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
// ============================================================================

View File

@ -78,9 +78,6 @@ public:
/** If this RewindInfo is an event. Subclasses will overwrite this. */
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. */
virtual bool isState() const { return false; }
// ------------------------------------------------------------------------
@ -121,18 +118,11 @@ public:
// ============================================================================
class RewindInfoState: public RewindInfoRewinder
{
private:
/** The 'left over' time from the physics. */
float m_local_physics_time;
public:
RewindInfoState(int ticks, Rewinder *rewinder,
BareNetworkString *buffer, bool is_confirmed);
virtual ~RewindInfoState() {};
// ------------------------------------------------------------------------
/** Returns the left-over physics time. */
float getLocalPhysicsTime() const { return m_local_physics_time; }
// ------------------------------------------------------------------------
virtual bool isState() const { return true; }
// ------------------------------------------------------------------------

View File

@ -25,7 +25,6 @@
#include "network/protocols/game_protocol.hpp"
#include "network/rewinder.hpp"
#include "network/rewind_info.hpp"
#include "network/time_step_info.hpp"
#include "physics/physics.hpp"
#include "race/history.hpp"
#include "utils/log.hpp"
@ -110,7 +109,7 @@ void RewindManager::addNextTimeStep(int time, float dt)
if ((time > 0 || m_rewind_queue.isEmpty()) &&
World::getWorld()->getPhase() != WorldStatus::IN_GAME_MENU_PHASE)
{
m_rewind_queue.addNewTimeStep(time, dt);
// m_rewind_queue.addNewTimeStep(time, dt);
}
} // addNextTimeStep
@ -122,8 +121,8 @@ void RewindManager::addNextTimeStep(int time, float dt)
* \param buffer Pointer to the event data.
*/
void RewindManager::addEvent(EventRewinder *event_rewinder,
BareNetworkString *buffer, bool confirmed,
int ticks)
BareNetworkString *buffer, bool confirmed,
int ticks)
{
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
// its own dt information (using TimeEvents).
m_rewind_queue.addNetworkState(m_all_rewinder[rewinder_index], buffer,
ticks, -99);
ticks);
} // addNetworkState
// ----------------------------------------------------------------------------
@ -192,24 +191,23 @@ void RewindManager::update(int ticks_not_used)
{
return;
}
PROFILER_PUSH_CPU_MARKER("RewindManager - save state", 0x20, 0x7F, 0x20);
// Save state
PROFILER_PUSH_CPU_MARKER("RewindManager - save state", 0x20, 0x7F, 0x20);
GameProtocol::lock()->startNewState();
AllRewinder::const_iterator 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();
if (buffer && buffer->size() >= 0)
{
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);
} // size >= 0
else
delete buffer; // NULL or 0 byte buffer
delete buffer; // buffer can be freed
}
PROFILER_POP_CPU_MARKER();
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;
} // update
#include "karts/abstract_kart.hpp"
// ----------------------------------------------------------------------------
/** 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 dt Number of time steps - should be 1.
* \param world_ticks Up to (and inclusive) which time events will be replayed.
* \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;
int rewind_ticks;
@ -232,43 +231,31 @@ void RewindManager::playEventsTill(float time, int *ticks)
// time step.
// merge and that have happened before the current time (which will
// be getTime()+dt - world time has not been updated yet).
m_rewind_queue.mergeNetworkData(World::getWorld()->getTimeTicks(),
&needs_rewind, &rewind_ticks);
m_rewind_queue.mergeNetworkData(world_ticks, &needs_rewind, &rewind_ticks);
if (needs_rewind)
{
Log::setPrefix("Rewind");
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();
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;
// This is necessary to avoid that rewinding an event will store the
// event again as a seemingly new event.
assert(!m_is_rewinding);
m_is_rewinding = true;
// Now play all events between time and time + dt, i.e. all events
// stored at the last TimeStep info in the rewind queue.
//assert(m_rewind_queue.getLast() == m_rewind_queue.getCurrent());
// Now play all events that happened at the current time stamp.
m_rewind_queue.replayAllEvents(world_ticks);
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;
} // playEventsTill
@ -277,8 +264,11 @@ void RewindManager::playEventsTill(float time, int *ticks)
* World::getTime() is reached again: it will replay everything before
* World::getTime(), but not the events at World::getTime() (or later)/
* \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);
bool is_history = history->replayHistory();
@ -297,26 +287,17 @@ void RewindManager::rewindTo(int rewind_ticks)
// Then undo the rewind infos going backwards in time
// --------------------------------------------------
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)
// ----------------------------
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:
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
// will not be increased while the game is paused
@ -327,30 +308,34 @@ void RewindManager::rewindTo(int rewind_ticks)
return;
}
// Restore state from the current time
current->replayAllStates();
// Get the (first) full state to which we have to rewind
RewindInfo *current = m_rewind_queue.getCurrent();
assert(current->isState());
// Now go forward through the list of rewind infos. A new timestep
// info for the current time has already been added previously, so
// we rewind till we have reached the last timestep entry (which is
// the current time step).
while (current != m_rewind_queue.getLast())
// Restore states from the exact rewind time
// -----------------------------------------
// A loop in case that we should split states into several smaller ones:
while (current && current->getTicks() == exact_rewind_ticks &&
current->isState() )
{
// Now handle all events(!) at the current time (i.e. between
// World::getTime() and World::getTime()+dt) before updating
// the world:
current->replayAllEvents();
dt = current->getDT();
current->rewind();
m_rewind_queue.next();
current = m_rewind_queue.getCurrent();
}
// 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);
#undef SHOW_ROLLBACK
#ifdef SHOW_ROLLBACK
irr_driver->update(dt);
irr_driver->update(stk_config->ticks2Time(1));
#endif
world->updateTime(1);
++m_rewind_queue;
current = m_rewind_queue.getCurrent();
world->setTicks(current->getTicks());
} // while (world->getTicks() < current_ticks)
// Now compute the errors which need to be visually smoothed

View File

@ -138,8 +138,8 @@ public:
void reset();
void update(int ticks);
void rewindTo(int target_ticks);
void playEventsTill(float time, int *ticks);
void rewindTo(int target_ticks, int ticks_now);
void playEventsTill(int world_ticks, int *ticks);
void addEvent(EventRewinder *event_rewinder, BareNetworkString *buffer,
bool confirmed, int ticks = -1);
void addNetworkEvent(EventRewinder *event_rewinder,

View File

@ -23,7 +23,6 @@
#include "network/network_config.hpp"
#include "network/rewind_info.hpp"
#include "network/rewind_manager.hpp"
#include "network/time_step_info.hpp"
#include <algorithm>
@ -43,7 +42,6 @@
*/
RewindQueue::RewindQueue()
{
m_current = m_time_step_info.begin();
reset();
} // RewindQueue
@ -53,14 +51,8 @@ RewindQueue::RewindQueue()
*/
RewindQueue::~RewindQueue()
{
// Destroying the
AllTimeStepInfo::const_iterator i;
for(i=m_time_step_info.begin(); i!=m_time_step_info.end(); ++i)
{
delete *i;
}
m_time_step_info.clear();
// This frees all current data
reset();
} // ~RewindQueue
// ----------------------------------------------------------------------------
@ -68,16 +60,6 @@ RewindQueue::~RewindQueue()
*/
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();
AllNetworkRewindInfo &info = m_network_events.getData();
@ -88,87 +70,50 @@ void RewindQueue::reset()
}
m_network_events.getData().clear();
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
// ----------------------------------------------------------------------------
/** 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.
* 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
* RewindInfo with the same time.
* will be insert at the front, and event info at the end of the RewindInfo
* with the same time.
* \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)
{
// FIXME: this should always be the last element in the list(??)
AllTimeStepInfo::iterator bucket = findPreviousTimeStepInfo(ri->getTicks());
AllRewindInfo::iterator i = m_all_rewind_info.end();
// FIXME: In case of a history replay an element could be inserted in the
// 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
// info was added. Since this is mostly for debugging, just ignore this
// this for now.
if(bucket!=m_time_step_info.end())
(*bucket)->insert(ri);
while (i != m_all_rewind_info.begin())
{
AllRewindInfo::iterator i_prev = i;
i_prev--;
// Now test if 'ri' needs to be inserted after the
// previous element, i.e. before the current element:
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
// ----------------------------------------------------------------------------
/** 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.
* \param ticks Time at which the event happened.
*/
void RewindQueue::addLocalEvent(EventRewinder *event_rewinder,
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
* being received from the servrer), or just a local state for
* 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,
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
* 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
* allocated and not freed by the caller!
* \param time Time at which the event was recorded.
* to m_tRewindInformation list by the main thread. The data to be stored
* must be allocated and not freed by the caller!
* \param buffer Pointer to the event data.
* \param ticks Time at which the event happened.
*/
void RewindQueue::addNetworkEvent(EventRewinder *event_rewinder,
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
* 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!
* \param time Time at which the event was recorded.
* \param buffer Pointer to the event data.
* \param ticks Time at which the event happened.
*/
void RewindQueue::addNetworkState(Rewinder *rewinder, BareNetworkString *buffer,
int ticks, float dt)
int ticks)
{
RewindInfo *ri = new RewindInfoState(ticks, rewinder,
buffer, /*confirmed*/true);
@ -237,19 +182,18 @@ void RewindQueue::addNetworkState(Rewinder *rewinder, BareNetworkString *buffer,
} // addNetworkState
// ----------------------------------------------------------------------------
/** Merges thread-safe all data received from the network with the current
* local rewind information.
/** Merges thread-safe all data received from the network up to and including
* the current time (tick) with the current local rewind information.
* \param world_ticks[in] Current world time up to which network events will be
* 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
* performed.
* \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
* might be modified in this function.
* must be performed (at least). Otherwise undefined.
*/
void RewindQueue::mergeNetworkData(int world_ticks,
bool *needs_rewind, int *rewind_ticks)
void RewindQueue::mergeNetworkData(int world_ticks, bool *needs_rewind,
int *rewind_ticks)
{
*needs_rewind = false;
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
// go through the whole list of events
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
// to be handled at the closest time to its original time. The current
// Ignore any events that will happen in the future. The current
// time step is 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)
{
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
// 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.
(*i)->setTicks(world_ticks);
}
// Find closest previous time step.
AllTimeStepInfo::iterator prev =
findPreviousTimeStepInfo((*i)->getTicks());
AllTimeStepInfo::iterator next = prev;
next++;
insertRewindInfo(*i);
int event_ticks = (*i)->getTicks();
Log::info("Rewind", "Inserting %s from time %d",
(*i)->isEvent() ? "event" : "state",
(*i)->getTicks() );
TimeStepInfo *tsi;
// Assign this event to the closest of the two existing timesteps
// 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())
// Check if a rewind is necessary, i.e. a message is received in the
// past of client (server never rewinds).
if (NetworkConfig::get()->isClient() && (*i)->getTicks() < world_ticks)
{
// We need rewind if we either receive an event in the past
// (FIXME: maybe we can just ignore this since we will also get
// a state update??), or receive a state from the current time
// (i.e. between world_time and world_time+dt). In the latter
// case we can just 'rewind' to this stage instead of doing a
// full simulation - though this client should potentially
// speed up a bit: if it receives a state from the server
// at the time the client is currently simulating (instead of
// triggering a rollback) it is not ahead enough of the server
// which will trigger a time adjustment from the server anyway.
if (tsi->getTicks() < world_ticks ||
(*i)->isState() && tsi == m_time_step_info.back())
{
*needs_rewind = true;
if (tsi->getTicks() > *rewind_ticks) *rewind_ticks = tsi->getTicks();
}
} // if client
// We need rewind if we receive an event in the past. This will
// then trigger a rewind later. Note that we only rewind to the
// latest event that happened earlier than 'now' - if there is
// more than one event in the past, we rewind to the last event.
// Since we restore a state before the rewind, this state will
// either include the earlier event or the state will be before
// the earlier event, and the event will be replayed anyway. This
// makes it easy to handle lost event messages.
*needs_rewind = true;
if ((*i)->getTicks() > *rewind_ticks)
*rewind_ticks = (*i)->getTicks();
} // if client and ticks < world_ticks
i = m_network_events.getData().erase(i);
} // for i in m_network_events
@ -353,7 +268,7 @@ void RewindQueue::mergeNetworkData(int world_ticks,
// ----------------------------------------------------------------------------
bool RewindQueue::isEmpty() const
{
return m_time_step_info.empty();
return m_current == m_all_rewind_info.end();
} // isEmpty
// ----------------------------------------------------------------------------
@ -361,53 +276,60 @@ bool RewindQueue::isEmpty() const
*/
bool RewindQueue::hasMoreRewindInfo() const
{
return m_current != m_time_step_info.end();
return m_current != m_all_rewind_info.end();
} // 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
* 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
* 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.
* undo_time and sets the internal 'current' pointer to this state.
* \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
(*m_current)->undoAll();
(*m_current)->undo();
if ((*m_current)->getTicks() <= undo_ticks &&
(*m_current)->hasConfirmedState())
if ( (*m_current)->getTicks() <= undo_ticks &&
(*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",
undo_ticks);
return -1;
} // 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:
* - 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();
// First tests: add a state first, then an event, and make
// sure the state stays first
RewindQueue q0;
assert(q0.isEmpty());
assert(!q0.hasMoreRewindInfo());
q0.addNewTimeStep(0, 0.5f);
q0.m_current = q0.m_time_step_info.begin();
assert(!q0.isEmpty());
q0.addLocalState(NULL, NULL, /*confirmed*/true, 0);
assert(q0.m_all_rewind_info.front()->isState());
assert(!q0.m_all_rewind_info.front()->isEvent());
assert(q0.hasMoreRewindInfo());
assert(q0.m_time_step_info.size() == 1);
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);
assert(q0.undoUntil(0) == 0);
q0.addNetworkEvent(dummy_rewinder, NULL, 0);
// Network events are not immediately merged
assert(q0.m_all_rewind_info.size() == 1);
bool needs_rewind;
int rewind_ticks;
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);
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
q0.addNetworkEvent(dummy_rewinder, NULL, 2);
dt = 0.01f; // to small, event from 0.2 will not be merged
// Another state must be sorted before the event:
q0.addNetworkState(dummy_rewinder, NULL, 0);
assert(q0.hasMoreRewindInfo());
q0.mergeNetworkData(world_ticks, &needs_rewind, &rewind_ticks);
assert(q0.m_time_step_info.size() == 2);
assert((*q0.m_time_step_info.begin())->getNumberOfEvents() == 2);
dt = 0.3f;
q0.mergeNetworkData(world_ticks, &needs_rewind, &rewind_ticks);
assert(q0.m_time_step_info.size() == 2);
assert((*q0.m_time_step_info.begin())->getNumberOfEvents() == 3);
assert(q0.m_all_rewind_info.size() == 3);
rii = q0.m_all_rewind_info.begin();
assert((*rii)->isState());
rii++;
assert((*rii)->isState());
rii++;
assert((*rii)->isEvent());
// This event will get added to the last time step info at 1.0:
q0.addNetworkEvent(dummy_rewinder, NULL, 1);
world_ticks = 8;
dt = 0.3f;
q0.mergeNetworkData(world_ticks, &needs_rewind, &rewind_ticks);
// Note that end() is behind the list, i.e. invalid, but rbegin()
// is the last element
assert((*q0.m_time_step_info.rbegin())->getNumberOfEvents() == 1);
// Test time base comparisons: adding an event to the end
q0.addLocalEvent(dummy_rewinder, NULL, true, 4);
// Then adding an earlier event
q0.addLocalEvent(dummy_rewinder, NULL, false, 1);
// rii points to the 3rd element, the ones added just now
// should be elements4 and 5:
rii++;
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
// ----------------
// 1) Current pointer was not reset from end of list when an event
// was added and the pointer was already at end of list
RewindQueue b1;
b1.addNewTimeStep(1, 0.1f);
++b1; // Should now point at end of list
b1.hasMoreRewindInfo();
b1.addNewTimeStep(2, 0.1f);
TimeStepInfo *tsi = b1.getCurrent();
assert(tsi->getTicks() == 2);
b1.addLocalEvent(NULL, NULL, true, 1);
b1.next(); // Should now point at end of list
assert(!b1.hasMoreRewindInfo());
b1.addLocalEvent(NULL, NULL, true, 2);
RewindInfo *ri = b1.getCurrent();
assert(ri->getTicks() == 2);
} // unitTesting

View File

@ -37,11 +37,10 @@ class TimeStepInfo;
class RewindQueue
{
private:
/** Pointer to all saved */
typedef std::list<TimeStepInfo*> AllTimeStepInfo;
/** The list of all events that are affected by a rewind. */
AllTimeStepInfo m_time_step_info;
typedef std::list<RewindInfo*> AllRewindInfo;
AllRewindInfo m_all_rewind_info;
/** The list of all events received from the network. They are stored
* in a separate thread (so this data structure is thread-save), and
@ -51,29 +50,17 @@ private:
typedef std::vector<RewindInfo*> AllNetworkRewindInfo;
Synchronised<AllNetworkRewindInfo> m_network_events;
/** Iterator to the curren time step info to be handled. This should
* always be at the same time as World::getTime(). */
AllTimeStepInfo::iterator m_current;
/** Iterator to the curren time step info to be handled. */
AllRewindInfo::iterator m_current;
AllTimeStepInfo::iterator findPreviousTimeStepInfo(int ticks);
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:
static void unitTesting();
RewindQueue();
~RewindQueue();
void reset();
void addNewTimeStep(int ticks, float dt);
void addLocalEvent(EventRewinder *event_rewinder, BareNetworkString *buffer,
bool confirmed, int ticks);
void addLocalState(Rewinder *rewinder, BareNetworkString *buffer,
@ -81,34 +68,30 @@ public:
void addNetworkEvent(EventRewinder *event_rewinder,
BareNetworkString *buffer, int ticks);
void addNetworkState(Rewinder *rewinder, BareNetworkString *buffer,
int ticks, float dt);
int ticks);
void mergeNetworkData(int world_ticks, bool *needs_rewind,
int *rewind_ticks);
void replayAllEvents(int ticks);
bool isEmpty() const;
bool hasMoreRewindInfo() const;
void 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(); }
int undoUntil(int undo_ticks);
// ------------------------------------------------------------------------
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++;
return m_current;
return;
} // operator++
// ------------------------------------------------------------------------
/** Returns the current RewindInfo. Caller must make sure that there is at least
* one more RewindInfo (see hasMoreRewindInfo()). */
TimeStepInfo *getCurrent()
/** Returns the current RewindInfo. Caller must make sure that there is at
* least one more RewindInfo (see hasMoreRewindInfo()). */
RewindInfo* getCurrent()
{
assert(m_current != m_time_step_info.end());
return *m_current;
return (m_current != m_all_rewind_info.end() ) ? *m_current : NULL;
} // getNext
}; // RewindQueue

View File

@ -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

View File

@ -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