Removed the storing of DT (which is not necessary when using ticks
now). Removed TimeStep information class etc.
This commit is contained in:
parent
17b3a79997
commit
2192d1383f
@ -1,5 +1,5 @@
|
||||
# Modify this file to change the last-modified date when you add/remove a file.
|
||||
# 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/*")
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 \
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
||||
// ============================================================================
|
||||
|
@ -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; }
|
||||
// ------------------------------------------------------------------------
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -1,101 +0,0 @@
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2013 Joerg Henrichs
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 3
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#include "network/time_step_info.hpp"
|
||||
|
||||
#include "network/rewind_info.hpp"
|
||||
#include "physics/physics.hpp"
|
||||
|
||||
/** Creates a new TimeStepInfo for a given time and given dt.
|
||||
* \param time Time for this TimeStepInfo object.
|
||||
* \param dt Time step size.
|
||||
*/
|
||||
TimeStepInfo::TimeStepInfo(int ticks, float dt)
|
||||
{
|
||||
m_ticks = ticks;
|
||||
m_dt = dt;
|
||||
// In case of unit testing physics does not exist
|
||||
if (Physics::getInstance())
|
||||
m_local_physics_time = Physics::getInstance()->getPhysicsWorld()
|
||||
->getLocalTime();
|
||||
else
|
||||
m_local_physics_time = 0.0f;
|
||||
} // StateEventList
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
/** Adds a state. State must be saved first, i.e. before events. The
|
||||
* RewindManager guarantees this order
|
||||
* \param ri The RewindInfo object for the state.
|
||||
*/
|
||||
void TimeStepInfo::insert(RewindInfo *ri)
|
||||
{
|
||||
if (ri->isState())
|
||||
{
|
||||
// States need to be inserted first.
|
||||
// FIXME: handle duplicated states, e.g. server doing a rewind
|
||||
// and sending another updated state
|
||||
AllRewindInfo::iterator i = m_list_of_events.begin();
|
||||
while (i != m_list_of_events.end() && (*i)->isState())
|
||||
++i;
|
||||
m_list_of_events.insert(i, ri);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Events at the same time are just added to the end
|
||||
m_list_of_events.push_back(ri);
|
||||
}
|
||||
} // insert
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
/** Undos all events and states for this time step.
|
||||
*/
|
||||
void TimeStepInfo::undoAll()
|
||||
{
|
||||
AllRewindInfo::reverse_iterator i;
|
||||
for (i = m_list_of_events.rbegin(); i != m_list_of_events.rend(); i++)
|
||||
{
|
||||
(*i)->undo();
|
||||
}
|
||||
} // undoAll
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
/** Replays all events for this TimeStepInfo.
|
||||
*/
|
||||
void TimeStepInfo::replayAllEvents()
|
||||
{
|
||||
AllRewindInfo::reverse_iterator i;
|
||||
for (i = m_list_of_events.rbegin(); i != m_list_of_events.rend(); i++)
|
||||
{
|
||||
if ((*i)->isEvent())
|
||||
(*i)->rewind();
|
||||
}
|
||||
} // replayAllEvents
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
/** Replays all state information for this TimeStepInfo.
|
||||
*/
|
||||
void TimeStepInfo::replayAllStates()
|
||||
{
|
||||
AllRewindInfo::reverse_iterator i;
|
||||
for (i = m_list_of_events.rbegin(); i != m_list_of_events.rend(); i++)
|
||||
{
|
||||
if ((*i)->isState())
|
||||
(*i)->rewind();
|
||||
}
|
||||
} // replayAllStates
|
||||
|
@ -1,102 +0,0 @@
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2017 Joerg Henrichs
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 3
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#ifndef HEADER_TIME_STEP_INFO_HPP
|
||||
#define HEADER_TIME_STEP_INFO_HPP
|
||||
|
||||
#include "network/rewind_info.hpp"
|
||||
#include <assert.h>
|
||||
#include <vector>
|
||||
|
||||
class RewindInfo;
|
||||
class EventRewinder;
|
||||
|
||||
/** \ingroup network
|
||||
*/
|
||||
|
||||
class RewindInfo;
|
||||
class RewindInfoEvent;
|
||||
class RewindInfoState;
|
||||
|
||||
/** This class stores information about each time step on a client or server.
|
||||
* Firstly it stores the world time and time step size. In case of a rewind
|
||||
* this allows the rewind to use the same time step size, which reduces
|
||||
* jitter caused by different time step size. Secondly for each time it
|
||||
* stores (if exist) all states, and all events. The TimeStepInfo acts as a
|
||||
* container and will store all states and events that happened 'around' time,
|
||||
* i.e. between time-X and time+Y (X and Y are implicitely defined in the
|
||||
* RewindQueue). This avoids that messages from clients to the server create
|
||||
* more and more TimeStepInfo, with smaller and smaller dt, which would make
|
||||
* rewinds more expensive.
|
||||
*/
|
||||
class TimeStepInfo
|
||||
{
|
||||
private:
|
||||
typedef std::vector<RewindInfo*> AllRewindInfo;
|
||||
|
||||
/** The list of all states and events at a certain time. */
|
||||
AllRewindInfo m_list_of_events;
|
||||
|
||||
/** Time at which those events should be executed here. */
|
||||
int m_ticks;
|
||||
|
||||
/** Time step to be used. */
|
||||
float m_dt;
|
||||
|
||||
/** Bullet maintains a 'left over' time since it is running with a fixed
|
||||
* 60 fps. Restoring this value exactly improves accuracy of rewinds. */
|
||||
float m_local_physics_time;
|
||||
public:
|
||||
TimeStepInfo(int ticks, float dt);
|
||||
void insert(RewindInfo *ri);
|
||||
void undoAll();
|
||||
void replayAllEvents();
|
||||
void replayAllStates();
|
||||
// ------------------------------------------------------------------------
|
||||
/** Sets the tiem of this object. */
|
||||
void setTicks(int ticks) { m_ticks = ticks; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Sets the time step size of this object. */
|
||||
void setDT(float dt) { m_dt = dt; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the time for this TimeStepInfo instance. */
|
||||
int getTicks() const { return m_ticks; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the left-over physics time. */
|
||||
float getLocalPhysicsTime() const { return m_local_physics_time; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the (previous) time step size, so that rewindw can be done
|
||||
* with same time step size. */
|
||||
float getDT() const { return m_dt; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns if this TimeStepInfo instance has a confirmed state, i.e. if
|
||||
* a rewind can start from this time. */
|
||||
bool hasConfirmedState() const
|
||||
{
|
||||
if (m_list_of_events.empty()) return false;
|
||||
const RewindInfo *ri = m_list_of_events[0];
|
||||
return ri->isState() && ri->isConfirmed();
|
||||
} // hasConfirmedState
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the number of events (and states) at this time step. Used
|
||||
* in unit testing. */
|
||||
int getNumberOfEvents() const { return m_list_of_events.size(); }
|
||||
}; // TimeStepInfo
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user