diff --git a/src/network/protocols/game_protocol.cpp b/src/network/protocols/game_protocol.cpp index 9f3f9eab0..820774e25 100644 --- a/src/network/protocols/game_protocol.cpp +++ b/src/network/protocols/game_protocol.cpp @@ -64,7 +64,7 @@ bool GameProtocol::notifyEventAsynchronous(Event* event) time, kart_id, action, value); BareNetworkString *s = new BareNetworkString(3); s->addUInt8(kart_id).addUInt8(action).addUInt16(value); - RewindManager::get()->addEvent(this, s, /*confirmed*/ true, time); + RewindManager::get()->addNetworkEvent(this, s, time); } if (data.size() > 0 ) diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index 1e9ca5a1c..cdad50941 100644 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -62,10 +62,11 @@ RewindManager::RewindManager() RewindManager::~RewindManager() { // Destroying the - for(unsigned int i=0; igetTime() > t) + while(i!=m_rewind_info.rend() && (*i)->getTime() > t) { #ifdef REWIND_SEARCH_STATS m_count_of_comparisons++; @@ -140,10 +150,10 @@ void RewindManager::insertRewindInfo(RewindInfo *ri) return; } - else // is a state + else // is a state or time { // If there are several infos for the same time t, - // a state must be inserted first + // a state or time entry must be inserted first AllRewindInfo::reverse_iterator i = m_rewind_info.rbegin(); while(i!=m_rewind_info.rend() && (*i)->getTime() >= t) { @@ -166,7 +176,8 @@ void RewindManager::insertRewindInfo(RewindInfo *ri) * \param time Time for which an index is searched. * \return Index in m_rewind_info after which to add rewind data. */ -unsigned int RewindManager::findFirstIndex(float target_time) const +RewindManager::AllRewindInfo::reverse_iterator + RewindManager::findFirstIndex(float target_time) { // For now do a linear search, even though m_rewind_info is sorted // I would expect that most insertions will be towards the (very) @@ -179,25 +190,25 @@ unsigned int RewindManager::findFirstIndex(float target_time) const #ifdef REWIND_SEARCH_STATS m_count_of_searches++; #endif - int index = m_rewind_info.size()-1; - int index_last_state = -1; - while(index>=0) + AllRewindInfo::reverse_iterator index = m_rewind_info.rbegin(); + AllRewindInfo::reverse_iterator index_last_state = m_rewind_info.rend(); + while(index!=m_rewind_info.rend()) { #ifdef REWIND_SEARCH_STATS m_count_of_comparisons++; #endif - if(m_rewind_info[index]->isState()) + if((*index)->isState()) { - if(m_rewind_info[index]->getTime()getTime()getTime()); + target_time, (*index_last_state)->getTime()); return index_last_state; // avoid compiler warning } // findFirstIndex @@ -235,6 +246,27 @@ void RewindManager::addEvent(EventRewinder *event_rewinder, insertRewindInfo(ri); } // addEvent +// ---------------------------------------------------------------------------- +/** 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_rewind_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. + * \param buffer Pointer to the event data. + */ +void RewindManager::addNetworkEvent(EventRewinder *event_rewinder, + BareNetworkString *buffer, float time) +{ + RewindInfo *ri = new RewindInfoEvent(time, event_rewinder, + buffer, /*confirmed*/true); + + // For now keep the freshly received events unsorted, they will + // be sorted when merged into m_rewind_info + m_network_events.lock(); + m_network_events.getData().push_back(ri); + m_network_events.unlock(); +} // addEvent + // ---------------------------------------------------------------------------- /** Determines if a new state snapshot should be taken, and if so calls all * rewinder to do so. @@ -257,14 +289,15 @@ void RewindManager::saveStates() } // For now always create a snapshot. - for(unsigned int i=0; isaveState(); + BareNetworkString *buffer = (*i)->saveState(); if(buffer && buffer->size()>=0) { m_overall_state_size += buffer->size(); RewindInfo *ri = new RewindInfoState(getCurrentTime(), - m_all_rewinder[i], buffer, + *i, buffer, /*is_confirmed*/true); assert(ri); insertRewindInfo(ri); @@ -287,11 +320,37 @@ void RewindManager::saveStates() */ void RewindManager::playEventsTill(float time) { + if (m_next_event== m_rewind_info.end()) + return; + + /** First merge all newly received network events into the main event list */ + float current_time = (*m_next_event)->getTime(); + bool rewind_necessary = false; + + AllRewindInfo::const_iterator i; + m_network_events.lock(); + for (i = m_network_events.getData().begin(); + i != m_network_events.getData().end(); i++) + { + if ((*i)->getTime() < current_time) + rewind_necessary = true; + if (rewind_necessary) + { + Log::info("RewindManager", "Rewind necessary at %f because of event at %f", + current_time, (*i)->getTime()); + } + insertRewindInfo(*i); + } + // Note that clear does not destruct the elements (which are now pointed + // to by m_rewind_info). + m_network_events.getData().clear(); + m_network_events.unlock(); + assert(!m_is_rewinding); m_is_rewinding = true; - while (m_next_event < m_rewind_info.size()) + while (m_next_event !=m_rewind_info.end() ) { - RewindInfo *ri = m_rewind_info[m_next_event]; + RewindInfo *ri = *m_next_event; if (ri->getTime() > time) { m_is_rewinding = false; @@ -317,9 +376,10 @@ void RewindManager::rewindTo(float rewind_time) // First find the state to which we need to rewind // ------------------------------------------------ - unsigned int index = findFirstIndex(rewind_time); + AllRewindInfo::reverse_iterator rindex = findFirstIndex(rewind_time); + AllRewindInfo::iterator index = --(rindex.base()); - if(!m_rewind_info[index]->isState()) + if(!(*index)->isState()) { Log::error("RewindManager", "No state for rewind to %f, state %d.", rewind_time, index); @@ -328,16 +388,17 @@ void RewindManager::rewindTo(float rewind_time) // Then undo the rewind infos going backwards in time // -------------------------------------------------- - for(int i=m_rewind_info.size()-1; i>=(int)index; i--) + AllRewindInfo::reverse_iterator i; + for(i= m_rewind_info.rbegin(); i!=rindex; i++) { - m_rewind_info[i]->undo(); + (*i)->undo(); // Now all states after the time we rewind to are not confirmed // anymore. They need to be rewritten when going forward during // the rewind. - if(m_rewind_info[i]->isState() && - m_rewind_info[i]->getTime() > m_rewind_info[index]->getTime() ) - m_rewind_info[i]->setConfirmed(false); + if((*i)->isState() && + (*i)->getTime() > (*index)->getTime() ) + (*i)->setConfirmed(false); } // for i>state @@ -348,7 +409,7 @@ void RewindManager::rewindTo(float rewind_time) // Get the (first) full state to which we have to rewind RewindInfoState *state = - dynamic_cast(m_rewind_info[index]); + dynamic_cast(*index); // Store the time to which we have to replay to float exact_rewind_time = state->getTime(); @@ -365,29 +426,28 @@ void RewindManager::rewindTo(float rewind_time) { state->rewind(); index++; - if(index>=m_rewind_info.size()) break; - state = dynamic_cast(m_rewind_info[index]); + if(index==m_rewind_info.end()) break; + state = dynamic_cast(*index); } // Now go forward through the list of rewind infos: // ------------------------------------------------ - while( world->getTime() < current_time && - index < (int)m_rewind_info.size() ) + while( world->getTime() < current_time && index !=m_rewind_info.end() ) { // Now handle all states and events at the current time before // updating the world: - while(index < (int)m_rewind_info.size() && - m_rewind_info[index]->getTime()<=world->getTime()+0.001f) + while(index !=m_rewind_info.end() && + (*index)->getTime()<=world->getTime()+0.001f) { - if(m_rewind_info[index]->isState()) + if((*index)->isState()) { // TOOD: replace the old state with a new state. // For now just set it to confirmed - m_rewind_info[index]->setConfirmed(true); + (*index)->setConfirmed(true); } - else if(m_rewind_info[index]->isEvent()) + else if((*index)->isEvent()) { - m_rewind_info[index]->rewind(); + (*index)->rewind(); } index++; } @@ -413,27 +473,17 @@ void RewindManager::rewindTo(float rewind_time) * return a dt that would be bigger tham this value. * \return The time step size to use in the next simulation step. */ -float RewindManager::determineTimeStepSize(int next_state, float end_time) +float RewindManager::determineTimeStepSize(AllRewindInfo::iterator next_state, + float end_time) { // If there is a next state (which is known to have a different time) // use the time difference to determine the time step size. - if(next_state < (int)m_rewind_info.size()) - return m_rewind_info[next_state]->getTime() - World::getWorld()->getTime(); + if(next_state !=m_rewind_info.end()) + return (*next_state)->getTime() - World::getWorld()->getTime(); // Otherwise, i.e. we are rewinding the last state/event, take the // difference between that time and the world time at which the rewind // was triggered. - return end_time - m_rewind_info[next_state-1]->getTime(); + return end_time - (*(--next_state))->getTime(); - - - float dt = 1.0f/60.0f; - float t = World::getWorld()->getTime(); - if(m_rewind_info[next_state]->getTime() < t + dt) - { - // Since we have RewindInfo at that time, it is certain that - /// this time is before (or at) end_time, not after. - return m_rewind_info[next_state]->getTime()-t; - } - return t+dt < end_time ? dt : end_time - t; } // determineTimeStepSize diff --git a/src/network/rewind_manager.hpp b/src/network/rewind_manager.hpp index 7e2cc498d..95b90ea14 100644 --- a/src/network/rewind_manager.hpp +++ b/src/network/rewind_manager.hpp @@ -21,8 +21,10 @@ #include "network/rewinder.hpp" #include "utils/ptr_vector.hpp" +#include "utils/synchronised.hpp" #include +#include #include class RewindInfo; @@ -89,12 +91,20 @@ private: AllRewinder m_all_rewinder; /** Pointer to all saved states. */ - typedef std::vector AllRewindInfo; + typedef std::list AllRewindInfo; + /** The list of all events that are affected by a rewind. */ AllRewindInfo m_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 + * merged into m_rewind_info from the main thread. This design (as + * opposed to locking m_rewind_info) reduces the synchronisation + * between main thread and network thread. */ + Synchronised m_network_events; + /** Index of the next event to be used when playing events. */ - unsigned int m_next_event; + AllRewindInfo::const_iterator m_next_event; /** Overall amount of memory allocated by states. */ unsigned int m_overall_state_size; @@ -125,9 +135,10 @@ private: RewindManager(); ~RewindManager(); - unsigned int findFirstIndex(float time) const; + AllRewindInfo::reverse_iterator findFirstIndex(float time); void insertRewindInfo(RewindInfo *ri); - float determineTimeStepSize(int state, float max_time); + float determineTimeStepSize(AllRewindInfo::iterator state, float max_time); + public: // First static functions to manage rewinding. // =========================================== @@ -156,6 +167,8 @@ public: void playEventsTill(float time); void addEvent(EventRewinder *event_rewinder, BareNetworkString *buffer, bool confirmed, float time = -1.0f); + void addNetworkEvent(EventRewinder *event_rewinder, + BareNetworkString *buffer, float time); // ------------------------------------------------------------------------ /** Sets the time that is to be used for all further states or events, * and the time step size. This is necessary so that states/events before