From 4de00657c91d3b6cbc4a37eaa3fcbb305cce9d99 Mon Sep 17 00:00:00 2001
From: Julian Smith <jules@op59.net>
Date: Mon, 18 May 2020 21:05:40 +0100
Subject: [PATCH] Multiplayer replay: added properties showing memory usage
 info.

---
 src/Aircraft/flightrecorder.cxx | 25 ++++++-------
 src/Aircraft/replay.cxx         | 63 +++++++++++++++++++++++++++++++++
 src/Aircraft/replay.hxx         | 27 ++++++++++++--
 src/Main/fg_init.cxx            |  2 ++
 4 files changed, 103 insertions(+), 14 deletions(-)

diff --git a/src/Aircraft/flightrecorder.cxx b/src/Aircraft/flightrecorder.cxx
index 255959bee..97f6ff125 100644
--- a/src/Aircraft/flightrecorder.cxx
+++ b/src/Aircraft/flightrecorder.cxx
@@ -317,18 +317,18 @@ FGFlightRecorder::processSignalList(const char* pSignalType, TSignalList& Signal
  * If pBuffer!=NULL memory of given buffer is reused.
  */
 FGReplayData*
-FGFlightRecorder::capture(double SimTime, FGReplayData* pRecycledBuffer)
+FGFlightRecorder::capture(double SimTime, FGReplayData* ReplayData)
 {
-    if (!pRecycledBuffer)
+    if (!ReplayData)
     {
-        pRecycledBuffer = new FGReplayData;
-        if (!pRecycledBuffer)
+        ReplayData = new FGReplayData;
+        if (!ReplayData)
             return NULL;
     }
     int Offset = 0;
-    pRecycledBuffer->sim_time = SimTime;
-    pRecycledBuffer->raw_data.resize( m_TotalRecordSize);
-    char* pBuffer = &pRecycledBuffer->raw_data.front();
+    ReplayData->sim_time = SimTime;
+    ReplayData->raw_data.resize( m_TotalRecordSize);
+    char* pBuffer = &ReplayData->raw_data.front();
     
     // 64bit aligned data first!
     {
@@ -407,10 +407,10 @@ FGFlightRecorder::capture(double SimTime, FGReplayData* pRecycledBuffer)
     
     // If m_ReplayMultiplayer is true, move all recent
     // multiplayer messages from m_MultiplayMgr into
-    // pRecycledBuffer->multiplayer_messages. Otherwise clear m_MultiplayMgr's
+    // ReplayData->multiplayer_messages. Otherwise clear m_MultiplayMgr's
     // list of recent messages.
     //
-    pRecycledBuffer->multiplayer_messages.clear();
+    ReplayData->multiplayer_messages.clear();
     bool    replayMultiplayer = m_ReplayMultiplayer->getBoolValue();
     for(;;) {
         auto MultiplayerMessage = m_MultiplayMgr->popMessageHistory();
@@ -418,11 +418,12 @@ FGFlightRecorder::capture(double SimTime, FGReplayData* pRecycledBuffer)
             break;
         }
         if (replayMultiplayer) {
-            pRecycledBuffer->multiplayer_messages.push_back( MultiplayerMessage);
+            ReplayData->multiplayer_messages.push_back( MultiplayerMessage);
         }
     }
-
-    return pRecycledBuffer;
+    ReplayData->UpdateStats();
+    
+    return ReplayData;
 }
 
 /** Do interpolation as defined by given interpolation type and weighting ratio. */
diff --git a/src/Aircraft/replay.cxx b/src/Aircraft/replay.cxx
index a7bee672a..e59d154c3 100644
--- a/src/Aircraft/replay.cxx
+++ b/src/Aircraft/replay.cxx
@@ -55,6 +55,69 @@ using simgear::gzContainerWriter;
 /** Magic string to verify valid FG flight recorder tapes. */
 static const char* const FlightRecorderFileMagic = "FlightGear Flight Recorder Tape";
 
+void FGReplayData::UpdateStats()
+{
+    size_t  bytes_raw_data_old = m_bytes_raw_data;
+    size_t  bytes_multiplayer_messages_old = m_bytes_multiplayer_messages;
+    size_t  num_multiplayer_messages_old = m_num_multiplayer_messages;
+    
+    m_bytes_raw_data = raw_data.size();
+    m_bytes_multiplayer_messages = 0;
+    for ( auto m: multiplayer_messages) {
+        m_bytes_multiplayer_messages += m->size();
+    }
+    m_num_multiplayer_messages = multiplayer_messages.size();
+    
+    // Update global stats by adding how much we have changed since last time
+    // UpdateStats() was called.
+    //
+    s_bytes_raw_data += m_bytes_raw_data - bytes_raw_data_old;
+    s_bytes_multiplayer_messages += m_bytes_multiplayer_messages - bytes_multiplayer_messages_old;
+    s_num_multiplayer_messages += m_num_multiplayer_messages - num_multiplayer_messages_old;
+    
+    if (!s_prop_num) s_prop_num = fgGetNode("/sim/replay/datastats_num", true);
+    if (!s_prop_bytes_raw_data) s_prop_bytes_raw_data = fgGetNode("/sim/replay/datastats_bytes_raw_data", true);
+    if (!s_prop_bytes_multiplayer_messages) s_prop_bytes_multiplayer_messages = fgGetNode("/sim/replay/datastats_bytes_multiplayer_messages", true);
+    if (!s_prop_num_multiplayer_messages) s_prop_num_multiplayer_messages = fgGetNode("/sim/replay/datastats_num_multiplayer_messages", true);
+    
+    s_prop_num->setLongValue(s_num);
+    s_prop_bytes_raw_data->setLongValue(s_bytes_raw_data);
+    s_prop_bytes_multiplayer_messages->setLongValue(s_bytes_multiplayer_messages);
+    s_prop_num_multiplayer_messages->setLongValue(s_num_multiplayer_messages);
+}
+
+FGReplayData::FGReplayData()
+{
+    s_num += 1;
+}
+
+FGReplayData::~FGReplayData()
+{
+    s_bytes_raw_data -= m_bytes_raw_data;
+    s_bytes_multiplayer_messages -= m_bytes_multiplayer_messages;
+    s_num_multiplayer_messages -= m_num_multiplayer_messages;
+    s_num -= 1;
+}
+
+void FGReplayData::resetStatisticsProperties()
+{
+    s_prop_num.reset();
+    s_prop_bytes_raw_data.reset();
+    s_prop_bytes_multiplayer_messages.reset();
+    s_prop_num_multiplayer_messages.reset();
+}
+
+size_t   FGReplayData::s_num = 0;
+size_t   FGReplayData::s_bytes_raw_data = 0;
+size_t   FGReplayData::s_bytes_multiplayer_messages = 0;
+size_t   FGReplayData::s_num_multiplayer_messages = 0;
+
+SGPropertyNode_ptr   FGReplayData::s_prop_num;
+SGPropertyNode_ptr   FGReplayData::s_prop_bytes_raw_data;
+SGPropertyNode_ptr   FGReplayData::s_prop_bytes_multiplayer_messages;
+SGPropertyNode_ptr   FGReplayData::s_prop_num_multiplayer_messages;
+
+
 namespace ReplayContainer
 {
     enum Type
diff --git a/src/Aircraft/replay.hxx b/src/Aircraft/replay.hxx
index c8e89ef27..d32238923 100644
--- a/src/Aircraft/replay.hxx
+++ b/src/Aircraft/replay.hxx
@@ -39,7 +39,7 @@
 
 class FGFlightRecorder;
 
-typedef struct {
+struct FGReplayData {
 
     double sim_time;
     // Our aircraft state.
@@ -47,7 +47,30 @@ typedef struct {
     
     // Incoming multiplayer messages.
     std::vector<std::shared_ptr<std::vector<char>>> multiplayer_messages;
-} FGReplayData;
+    
+    // Updates static statistics defined below. 
+    void UpdateStats();
+
+    // Resets out static property nodes; to be called by fgStartNewReset().
+    static void resetStatisticsProperties();
+    
+    FGReplayData();
+    ~FGReplayData();
+    
+    size_t  m_bytes_raw_data = 0;
+    size_t  m_bytes_multiplayer_messages = 0;
+    size_t  m_num_multiplayer_messages = 0;
+    
+    // Statistics about replay data, also properties /sim/replay/datastats_*.
+    static size_t   s_num;
+    static size_t   s_bytes_raw_data;
+    static size_t   s_bytes_multiplayer_messages;
+    static size_t   s_num_multiplayer_messages;
+    static SGPropertyNode_ptr   s_prop_num;
+    static SGPropertyNode_ptr   s_prop_bytes_raw_data;
+    static SGPropertyNode_ptr   s_prop_bytes_multiplayer_messages;
+    static SGPropertyNode_ptr   s_prop_num_multiplayer_messages;
+};
 
 typedef struct {
     double sim_time;
diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx
index 147102837..8e566ee19 100644
--- a/src/Main/fg_init.cxx
+++ b/src/Main/fg_init.cxx
@@ -1359,6 +1359,8 @@ void fgStartNewReset()
     
     fgOSResetProperties();
 
+    FGReplayData::resetStatisticsProperties();
+
 // init some things manually
 // which do not follow the regular init pattern