From a9a73e643bda6b8b2a76bfbb1bb243d260c1d672 Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 17 Apr 2018 18:42:16 +1000 Subject: [PATCH] Save an initial state at t=0 on the client, to make sure they can always rewind (e.g. in case that an event from another client arrives before a state from the server). --- src/modes/world_status.cpp | 8 +++++++ src/network/protocols/game_protocol.cpp | 19 +++++++++------- src/network/protocols/game_protocol.hpp | 7 ++++-- src/network/rewind_manager.cpp | 29 ++++++++++++++++++++----- src/network/rewind_manager.hpp | 3 ++- 5 files changed, 50 insertions(+), 16 deletions(-) diff --git a/src/modes/world_status.cpp b/src/modes/world_status.cpp index 481f5a1aa..d4203cb1a 100644 --- a/src/modes/world_status.cpp +++ b/src/modes/world_status.cpp @@ -302,6 +302,14 @@ void WorldStatus::updateTime(int ticks) { // set phase is over, go to the next one m_phase = GO_PHASE; + // Save one initial state on a client, in case that an event + // is received from a client (trieggering a rollback) before + // a state from the server has been received. + if (NetworkConfig::get()->isClient()) + { + RewindManager::get()->saveLocalState(); + // FIXME TODO: save state in rewind queue! + } if (m_play_ready_set_go_sounds) { m_start_sound->play(); diff --git a/src/network/protocols/game_protocol.cpp b/src/network/protocols/game_protocol.cpp index 1c2701df1..3d1d5dc86 100644 --- a/src/network/protocols/game_protocol.cpp +++ b/src/network/protocols/game_protocol.cpp @@ -245,18 +245,22 @@ void GameProtocol::handleAdjustTime(Event *event) // ---------------------------------------------------------------------------- /** Called by the server before assembling a new message containing the full * state of the race to be sent to a client. - * \param allow_local_save If set it allows a state to be saved on a client. + * \param local_save If set it allows a state to be saved on a client. * This only happens at the very first time step to ensure each client * has a state in case it receives an event before a server state. */ -void GameProtocol::startNewState(bool allow_local_save) +void GameProtocol::startNewState(bool local_save) { - assert(allow_local_save || NetworkConfig::get()->isServer()); + assert(local_save || NetworkConfig::get()->isServer()); m_data_to_send->clear(); - m_data_to_send->addUInt8(GP_STATE).addUInt32(World::getWorld()->getTimeTicks()); - Log::info("GameProtocol", "Starting new state at %d.", - World::getWorld()->getTimeTicks()); + // Local saves don't neet this info, they pass time directly to the + // RewindInfo in RewindManager::saveLocalState. + if (!local_save) + { + m_data_to_send->addUInt8(GP_STATE) + .addUInt32(World::getWorld()->getTimeTicks()); + } } // startNewState // ---------------------------------------------------------------------------- @@ -264,9 +268,8 @@ void GameProtocol::startNewState(bool allow_local_save) * 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(bool allow_local_save, BareNetworkString *buffer) +void GameProtocol::addState(BareNetworkString *buffer) { - assert(allow_local_save || NetworkConfig::get()->isServer()); m_data_to_send->addUInt16(buffer->size()); (*m_data_to_send) += *buffer; } // addState diff --git a/src/network/protocols/game_protocol.hpp b/src/network/protocols/game_protocol.hpp index a821597c7..5e3af2f72 100644 --- a/src/network/protocols/game_protocol.hpp +++ b/src/network/protocols/game_protocol.hpp @@ -78,8 +78,8 @@ public: void controllerAction(int kart_id, PlayerAction action, int value, int val_l, int val_r); - void startNewState(bool allow_local_save); - void addState(bool allow_local_save, BareNetworkString *buffer); + void startNewState(bool local_save); + void addState(BareNetworkString *buffer); void sendState(); void adjustTimeForClient(STKPeer *peer, int ticks); @@ -101,6 +101,9 @@ public: { return m_game_protocol.lock(); } // lock + // ------------------------------------------------------------------------ + /** Returns the NetworkString in which a state was saved. */ + NetworkString* getState() const { return m_data_to_send; } }; // class GameProtocol diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index 3d5a74792..ffe0b1118 100755 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -172,10 +172,10 @@ void RewindManager::addNetworkState(BareNetworkString *buffer, int ticks) * a client before a state from the serer). * \param allow_local_save Do a local save. */ -void RewindManager::saveState(bool allow_local_save) +void RewindManager::saveState(bool local_save) { PROFILER_PUSH_CPU_MARKER("RewindManager - save state", 0x20, 0x7F, 0x20); - GameProtocol::lock()->startNewState(allow_local_save); + GameProtocol::lock()->startNewState(local_save); AllRewinder::const_iterator rewinder; for (rewinder = m_all_rewinder.begin(); rewinder != m_all_rewinder.end(); ++rewinder) { @@ -185,15 +185,34 @@ void RewindManager::saveState(bool allow_local_save) if (buffer && buffer->size() >= 0) { m_overall_state_size += buffer->size(); - GameProtocol::lock()->addState(allow_local_save, buffer); + GameProtocol::lock()->addState(buffer); } // size >= 0 delete buffer; // buffer can be freed } PROFILER_POP_CPU_MARKER(); - } // saveState - // ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- +/** Saves a state on the client. Used to save an initial state at t=0 for each + * client in case that we receive an event from another client (which will + * trigger a rewind) before a state from the server. + */ +void RewindManager::saveLocalState() +{ + int ticks = World::getWorld()->getTimeTicks(); + + saveState(/*local_state*/true); + NetworkString *state = GameProtocol::lock()->getState(); + + // Copy the data to a new string, making the buffer in + // GameProtocol availble for again. + BareNetworkString *bns = + new BareNetworkString(state->getCurrentData(), + state->size() ); + m_rewind_queue.addLocalState(bns, /*confirmed*/true, ticks); +} // saveLocalState + +// ---------------------------------------------------------------------------- /** Restores a given state by calling rewindToState for each available rewinder * with its correct data. * \param data The data string used to store the whole game state. diff --git a/src/network/rewind_manager.hpp b/src/network/rewind_manager.hpp index 4ff87e297..2662f0d78 100644 --- a/src/network/rewind_manager.hpp +++ b/src/network/rewind_manager.hpp @@ -146,7 +146,8 @@ public: BareNetworkString *buffer, int ticks); void addNetworkState(BareNetworkString *buffer, int ticks); void addNextTimeStep(int ticks, float dt); - void saveState(bool allow_local_save); + void saveState(bool local_save); + void saveLocalState(); void restoreState(BareNetworkString *buffer); // ------------------------------------------------------------------------ /** Adds a Rewinder to the list of all rewinders.