From 4553d813b1f20f966d2d5e3cbe10ba9f9bb0e6d1 Mon Sep 17 00:00:00 2001 From: Julian Smith Date: Mon, 22 Feb 2021 23:16:26 +0000 Subject: [PATCH] Various fixes and improvements to replaying of Continuous recordings. FGFlightRecorder::replay(): Fixed behaviour when going back in time while replaying - we were not replaying initial property changes correctly because we deliberately don't load signals data for some frames when we are only interested in multiplayer or extra properties. Removed incorrect code that tried to avoid replaying the same frame twice in succession. FGReplay::update(double dt): Improved how we figure out which frames we need to replay just for multiplayer or extra property changes. FGReplay::makeTapePath(): New fn containing the code for converting tape name into path by prepending tape dir and appending .fgtape as necessary. Called when loading tape at startup or at runtime. Cached some more property nodes in SGPropertyNode_ptr's. Don't recordCHAT_MSG_ID messages; unfortunately it looks like most (all?) chat message text is received as part of POS_DATA_ID messages tied to sim/multiplay/chat, so this doesn't actually avoid recording chat messages. --- src/Aircraft/flightrecorder.cxx | 244 +++++++++++++----------- src/Aircraft/flightrecorder.hxx | 6 + src/Aircraft/replay.cxx | 201 ++++++++++++-------- src/Aircraft/replay.hxx | 5 + src/Main/options.cxx | 3 +- src/MultiPlayer/multiplaymgr.cxx | 314 +++++++++++++++---------------- 6 files changed, 424 insertions(+), 349 deletions(-) diff --git a/src/Aircraft/flightrecorder.cxx b/src/Aircraft/flightrecorder.cxx index 13df76db8..520f27b4d 100644 --- a/src/Aircraft/flightrecorder.cxx +++ b/src/Aircraft/flightrecorder.cxx @@ -222,10 +222,14 @@ static std::shared_ptr s_record_extra_properties; FGFlightRecorder::FGFlightRecorder(const char* pConfigName) : - m_RecorderNode(fgGetNode("/sim/flight-recorder", true)), - m_ReplayMultiplayer(fgGetNode("/sim/replay/multiplayer", true)), - m_RecordContinuous(fgGetNode("/sim/replay/record-continuous", true)), - m_RecordExtraProperties(fgGetNode("/sim/replay/record-extra-properties", true)), + m_RecorderNode (fgGetNode("/sim/flight-recorder", true)), + m_ReplayMultiplayer (fgGetNode("/sim/replay/multiplayer", true)), + m_ReplayExtraProperties (fgGetNode("/sim/replay/replay-extra-properties", true)), + m_ReplayMainView (fgGetNode("/sim/replay/replay-main-view", true)), + m_ReplayMainWindowPosition (fgGetNode("/sim/replay/replay-main-window-position", true)), + m_ReplayMainWindowSize (fgGetNode("/sim/replay/replay-main-window-size", true)), + m_RecordContinuous (fgGetNode("/sim/replay/record-continuous", true)), + m_RecordExtraProperties (fgGetNode("/sim/replay/record-extra-properties", true)), m_TotalRecordSize(0), m_ConfigName(pConfigName), m_usingDefaultConfig(false), @@ -624,12 +628,29 @@ FGFlightRecorder::capture(double SimTime, FGReplayData* ReplayData) ReplayData->multiplayer_messages.clear(); bool replayMultiplayer = m_ReplayMultiplayer->getBoolValue(); for(;;) { - auto MultiplayerMessage = m_MultiplayMgr->popMessageHistory(); - if (!MultiplayerMessage) { + auto multiplayerMessage = m_MultiplayMgr->popMessageHistory(); + if (!multiplayerMessage) { break; } if (replayMultiplayer) { - ReplayData->multiplayer_messages.push_back( MultiplayerMessage); + // Attempt to ignore chat messages. Unfortunately it seems + // that CHAT_MSG_ID is unused, and instead chat messages are + // included in POS_DATA_ID messages as sim/multiplay/chat in + // src/MultiPlayer/multiplaymgr.cxx's sIdPropertyList. + // + auto message_header = reinterpret_cast(&multiplayerMessage->front()); + xdr_data_t message_id = message_header->MsgId; + if (message_id == CHAT_MSG_ID) { + SG_LOG(SG_GENERAL, SG_ALERT, "Not recording chat message: " + << std::string( + reinterpret_cast(message_header + 1)->Text, + message_header->MsgLen - sizeof(*message_header) + ) + ); + } + else { + ReplayData->multiplayer_messages.push_back( multiplayerMessage); + } } } @@ -637,7 +658,7 @@ FGFlightRecorder::capture(double SimTime, FGReplayData* ReplayData) // if (m_RecordContinuous->getBoolValue()) { if (!m_RecordExtraPropertiesReference) { - SG_LOG(SG_SYSTEMS, SG_ALERT, "m_RecordPropertiesReference is null"); + SG_LOG(SG_SYSTEMS, SG_DEBUG, "m_RecordPropertiesReference is null"); m_RecordExtraPropertiesReference = new SGPropertyNode; } s_record_extra_properties->capture(m_RecordExtraPropertiesReference, ReplayData); @@ -698,7 +719,7 @@ weighting(TInterpolation interpolation, double ratio, double v1,double v2) void FGFlightRecorder::resetExtraProperties() { - SG_LOG(SG_SYSTEMS, SG_ALERT, "Clearing m_RecordExtraPropertiesReference"); + SG_LOG(SG_SYSTEMS, SG_DEBUG, "Clearing m_RecordExtraPropertiesReference"); m_RecordExtraPropertiesReference = nullptr; } @@ -726,135 +747,128 @@ FGFlightRecorder::replay(double SimTime, const FGReplayData* _pNextBuffer, int* main_window_ysize ) { - const char* pLastBuffer = (_pLastBuffer) ? &_pLastBuffer->raw_data.front() : nullptr; - const char* pBuffer = (_pNextBuffer) ? &_pNextBuffer->raw_data.front() : nullptr; - if (!pBuffer) - return; - - int Offset = 0; - double ratio = 1.0; - if (pLastBuffer) - { - double NextSimTime = _pNextBuffer->sim_time; - double LastSimTime = _pLastBuffer->sim_time; - double Numerator = SimTime - LastSimTime; - double dt = NextSimTime - LastSimTime; - // avoid divide by zero and other quirks - if ((Numerator > 0.0)&&(dt != 0.0)) + const char* pLastBuffer = (_pLastBuffer && !_pLastBuffer->raw_data.empty()) ? &_pLastBuffer->raw_data.front() : nullptr; + const char* pBuffer = (_pNextBuffer && !_pNextBuffer->raw_data.empty()) ? &_pNextBuffer->raw_data.front() : nullptr; + + if (pBuffer) { + /* Replay signals. */ + int Offset = 0; + double ratio = 1.0; + if (pLastBuffer) { - ratio = Numerator / dt; - if (ratio > 1.0) - ratio = 1.0; - } - } - - // 64bit aligned data first! - { - // restore doubles - const double* pDoubles = (const double*) &pBuffer[Offset]; - const double* pLastDoubles = (const double*) &pLastBuffer[Offset]; - unsigned int SignalCount = m_CaptureDouble.size(); - for (unsigned int i=0; isim_time; + double LastSimTime = _pLastBuffer->sim_time; + double Numerator = SimTime - LastSimTime; + double dt = NextSimTime - LastSimTime; + // avoid divide by zero and other quirks + if ((Numerator > 0.0)&&(dt != 0.0)) { - v = weighting(m_CaptureDouble[i].Interpolation, ratio, - pLastDoubles[i], v); + ratio = Numerator / dt; + if (ratio > 1.0) + ratio = 1.0; } - m_CaptureDouble[i].Signal->setDoubleValue(v); } - Offset += SignalCount * sizeof(double); - } - // 32bit aligned data comes second... - { - // restore floats - const float* pFloats = (const float*) &pBuffer[Offset]; - const float* pLastFloats = (const float*) &pLastBuffer[Offset]; - unsigned int SignalCount = m_CaptureFloat.size(); - for (unsigned int i=0; isetDoubleValue(v); } - m_CaptureFloat[i].Signal->setDoubleValue(v);//setFloatValue + Offset += SignalCount * sizeof(double); } - Offset += SignalCount * sizeof(float); - } - { - // restore integers (32bit aligned) - const int* pInt = (const int*) &pBuffer[Offset]; - unsigned int SignalCount = m_CaptureInteger.size(); - for (unsigned int i=0; isetIntValue(pInt[i]); + // restore floats + const float* pFloats = (const float*) &pBuffer[Offset]; + const float* pLastFloats = (const float*) &pLastBuffer[Offset]; + unsigned int SignalCount = m_CaptureFloat.size(); + for (unsigned int i=0; isetDoubleValue(v);//setFloatValue + } + Offset += SignalCount * sizeof(float); } - Offset += SignalCount * sizeof(int); - } - // 16bit aligned data is next... - { - // restore 16bit short integers - const short int* pShortInt = (const short int*) &pBuffer[Offset]; - unsigned int SignalCount = m_CaptureInt16.size(); - for (unsigned int i=0; isetIntValue(pShortInt[i]); + // restore integers (32bit aligned) + const int* pInt = (const int*) &pBuffer[Offset]; + unsigned int SignalCount = m_CaptureInteger.size(); + for (unsigned int i=0; isetIntValue(pInt[i]); + } + Offset += SignalCount * sizeof(int); } - Offset += SignalCount * sizeof(short int); - } - // finally: byte aligned data is last... - { - // restore 8bit chars - const signed char* pChar = (const signed char*) &pBuffer[Offset]; - unsigned int SignalCount = m_CaptureInt8.size(); - for (unsigned int i=0; isetIntValue(pChar[i]); + // restore 16bit short integers + const short int* pShortInt = (const short int*) &pBuffer[Offset]; + unsigned int SignalCount = m_CaptureInt16.size(); + for (unsigned int i=0; isetIntValue(pShortInt[i]); + } + Offset += SignalCount * sizeof(short int); } - Offset += SignalCount * sizeof(signed char); - } - { - // restore 1bit booleans (8bit aligned) - const unsigned char* pFlags = (const unsigned char*) &pBuffer[Offset]; - unsigned int SignalCount = m_CaptureBool.size(); - int Size = (SignalCount+7)/8; - Offset += Size; - for (unsigned int i=0; isetBoolValue(0 != (pFlags[i>>3] & (1 << (i&7)))); + // restore 8bit chars + const signed char* pChar = (const signed char*) &pBuffer[Offset]; + unsigned int SignalCount = m_CaptureInt8.size(); + for (unsigned int i=0; isetIntValue(pChar[i]); + } + Offset += SignalCount * sizeof(signed char); + } + + { + // restore 1bit booleans (8bit aligned) + const unsigned char* pFlags = (const unsigned char*) &pBuffer[Offset]; + unsigned int SignalCount = m_CaptureBool.size(); + int Size = (SignalCount+7)/8; + Offset += Size; + for (unsigned int i=0; isetBoolValue(0 != (pFlags[i>>3] & (1 << (i&7)))); + } } } - // Replay any multiplayer messages. But don't send the same multiplayer - // messages repeatedly when we are called with a timestamp that ends up - // picking the same _pNextBuffer as last time. + // Replay any multiplayer messages. + for (auto multiplayer_message: _pNextBuffer->multiplayer_messages) { + SG_LOG(SG_SYSTEMS, SG_DEBUG, "Pushing multiplayer message to multiplay manager"); + m_MultiplayMgr->pushMessageHistory(multiplayer_message); + } + + // Replay extra property changes. // - static const FGReplayData* _pNextBuffer_prev = nullptr; - if ( _pNextBuffer != _pNextBuffer_prev) { - _pNextBuffer_prev = _pNextBuffer; - for (auto multiplayer_message: _pNextBuffer->multiplayer_messages) { - m_MultiplayMgr->pushMessageHistory(multiplayer_message); - } - } + bool replay_extra_properties = m_ReplayExtraProperties->getBoolValue(); + bool replay_main_view = m_ReplayMainView->getBoolValue(); + bool replay_main_window_position = m_ReplayMainWindowPosition->getBoolValue(); + bool replay_main_window_size = m_ReplayMainWindowSize->getBoolValue(); - // Replay property changes. - // - - bool replay_extra_property_removal = globals->get_props()->getBoolValue("sim/replay/replay-extra-property-removal"); - bool replay_extra_property_changes = globals->get_props()->getBoolValue("sim/replay/replay-extra-property-changes"); - bool replay_main_view = globals->get_props()->getBoolValue("sim/replay/replay-main-view"); - bool replay_main_window_position = globals->get_props()->getBoolValue("sim/replay/replay-main-window-position"); - bool replay_main_window_size = globals->get_props()->getBoolValue("sim/replay/replay-main-window-size"); - - if (replay_extra_property_removal) { + if (replay_extra_properties) { for (auto extra_property_removed_path: _pNextBuffer->replay_extra_property_removals) { SG_LOG(SG_SYSTEMS, SG_DEBUG, "replaying extra property removal: " << extra_property_removed_path); globals->get_props()->removeChild(extra_property_removed_path); @@ -875,7 +889,7 @@ FGFlightRecorder::replay(double SimTime, const FGReplayData* _pNextBuffer, const std::string& path = prop_change.first; const std::string& value = prop_change.second; if (simgear::strutils::starts_with(path, "/sim/current-view/view-number")) { - SG_LOG(SG_SYSTEMS, SG_DEBUG, "*** SimTime=" << SimTime << " replaying view " << path << "=" << value); + SG_LOG(SG_SYSTEMS, SG_DEBUG, "SimTime=" << SimTime << " replaying view " << path << "=" << value); globals->get_props()->setStringValue(path, value); } } @@ -905,7 +919,7 @@ FGFlightRecorder::replay(double SimTime, const FGReplayData* _pNextBuffer, globals->get_props()->setStringValue(path, value); } } - else if (replay_extra_property_changes) { + else if (replay_extra_properties) { SG_LOG(SG_SYSTEMS, SG_DEBUG, "SimTime=" << SimTime << " replaying extra_property change: " << path << "=" << value); globals->get_props()->setStringValue(path, value); diff --git a/src/Aircraft/flightrecorder.hxx b/src/Aircraft/flightrecorder.hxx index 4573f5314..4f6267d15 100644 --- a/src/Aircraft/flightrecorder.hxx +++ b/src/Aircraft/flightrecorder.hxx @@ -85,7 +85,13 @@ private: SGPropertyNode_ptr m_RecorderNode; SGPropertyNode_ptr m_ConfigNode; + SGPropertyNode_ptr m_ReplayMultiplayer; + SGPropertyNode_ptr m_ReplayExtraProperties; + SGPropertyNode_ptr m_ReplayMainView; + SGPropertyNode_ptr m_ReplayMainWindowPosition; + SGPropertyNode_ptr m_ReplayMainWindowSize; + SGPropertyNode_ptr m_RecordContinuous; SGPropertyNode_ptr m_RecordExtraProperties; diff --git a/src/Aircraft/replay.cxx b/src/Aircraft/replay.cxx index 0b3b36e45..7be6eb747 100644 --- a/src/Aircraft/replay.cxx +++ b/src/Aircraft/replay.cxx @@ -739,7 +739,7 @@ static SGPropertyNode_ptr saveSetup( || fgGetBool("/sim/replay/record-main-window", false) || fgGetBool("/sim/replay/record-main-view", false) ) { - SG_LOG(SG_SYSTEMS, SG_ALERT, "Adding data[]=extra-properties." + SG_LOG(SG_SYSTEMS, SG_DEBUG, "Adding data[]=extra-properties." << " record-extra-properties=" << fgGetBool("/sim/replay/record-extra-properties", false) << " record-main-window=" << fgGetBool("/sim/replay/record-main-window", false) << " record-main-view=" << fgGetBool("/sim/replay/record-main-view", false) @@ -852,6 +852,15 @@ struct FGFrameInfo bool has_extra_properties = false; }; +std::ostream& operator << (std::ostream& out, const FGFrameInfo& frame_info) +{ + return out << "{" + << " offset=" << frame_info.offset + << " has_multiplayer=" << frame_info.has_multiplayer + << " has_extra_properties=" << frame_info.has_extra_properties + << "}"; +} + void FGReplay::update( double dt ) { @@ -1257,69 +1266,44 @@ FGReplay::replay( double time ) { int xsize = xsize0; int ysize = ysize0; - double t_begin = m_continuous_in_time_last; - if (m_continuous_in_extra_properties) { - // Continuous recording has property changes. + double multiplayer_recent = 3; + + // We replay all frames from just after the previously-replayed frame, + // in order to replay extra properties and multiplayer aircraft + // correctly. + // + double t_begin = m_continuous_in_frame_time_last; + + if (time < m_continuous_in_time_last) { + // We have gone backwards, e.g. user has clicked on the back + // buttons in the Replay dialogue. // - if (time < m_continuous_in_time_last) { - // We have gone backwards; need to replay all property changes - // from t=0. + + if (m_continuous_in_multiplayer) { + // Continuous recording has multiplayer data, so replay recent + // ones. + // + t_begin = time - multiplayer_recent; + } + + if (m_continuous_in_extra_properties) { + // Continuous recording has property changes. we need to replay + // all property changes from the beginning. + // t_begin = -1; } - } - - double multiplayer_recent = 3; - if (m_continuous_in_multiplayer) { - double t = std::max(0.0, time - multiplayer_recent); - t_begin = std::min(t_begin, t); - } - - // Replay property changes for all t in t_prop_begin < t < time, and multiplayer - // changes for most recent multiplayer_recent seconds. - // - for (auto p = m_continuous_in_time_to_frameinfo.upper_bound(t_begin); - p != m_continuous_in_time_to_frameinfo.end(); - ++p) - { - if (p->first >= time) break; - SG_LOG(SG_SYSTEMS, SG_DEBUG, "Replaying extra property changes." + + SG_LOG(SG_SYSTEMS, SG_DEBUG, "Have gone backwards." << " m_continuous_in_time_last=" << m_continuous_in_time_last << " time=" << time << " t_begin=" << t_begin - << " p->first=" << p->first - << " p->second.offset=" << p->second.offset + << " m_continuous_in_extra_properties=" << m_continuous_in_extra_properties ); - - // Replaying a frame is expensive because we read frame data - // from disc each time. So we only replay this frame if it has - // extra_properties, or if it has multiplayer packets and we are - // within seconds of current time. - // - bool replay_this_frame = p->second.has_extra_properties; - if (!replay_this_frame) { - if (p->first > time - multiplayer_recent) { - if (p->second.has_multiplayer) { - replay_this_frame = true; - } - } - } - if (replay_this_frame) { - replay( - p->first, - p->second.offset, - 0 /*offset_old*/, - false /*replay_signals*/, - p->first > time - multiplayer_recent /*replay_multiplayer*/, - true /*replay_extra_properties*/, - &xpos, - &ypos, - &xsize, - &ysize - ); - } } - // Replay from uncompressed recording file. + // Prepare to replay signals from uncompressed recording file. We want + // to find a pair of frames that straddle the requested