diff --git a/src/Aircraft/flightrecorder.cxx b/src/Aircraft/flightrecorder.cxx
index 9ac2169dd..255959bee 100644
--- a/src/Aircraft/flightrecorder.cxx
+++ b/src/Aircraft/flightrecorder.cxx
@@ -36,15 +36,19 @@
 #include <simgear/math/SGMath.hxx>
 #include <Main/fg_props.hxx>
 #include "flightrecorder.hxx"
+#include <MultiPlayer/multiplaymgr.hxx>
+#include <MultiPlayer/mpmessages.hxx>
 
 using namespace FlightRecorder;
 using std::string;
 
 FGFlightRecorder::FGFlightRecorder(const char* pConfigName) :
     m_RecorderNode(fgGetNode("/sim/flight-recorder", true)),
+    m_ReplayMultiplayer(fgGetNode("/sim/replay/multiplayer", true)),
     m_TotalRecordSize(0),
     m_ConfigName(pConfigName),
-    m_usingDefaultConfig(false)
+    m_usingDefaultConfig(false),
+    m_MultiplayMgr(globals->get_subsystem<FGMultiplayMgr>())
 {
 }
 
@@ -308,23 +312,6 @@ FGFlightRecorder::processSignalList(const char* pSignalType, TSignalList& Signal
     }
 }
 
-/** Get an empty container for a single capture. */
-FGReplayData*
-FGFlightRecorder::createEmptyRecord(void)
-{
-    if (!m_TotalRecordSize)
-        return NULL;
-    FGReplayData* p = (FGReplayData*) new unsigned char[m_TotalRecordSize];
-    return p;
-}
-
-/** Free given container with capture data. */
-void
-FGFlightRecorder::deleteRecord(FGReplayData* pRecord)
-{
-    delete[] pRecord;
-}
-
 /** Capture data.
  * When pBuffer==NULL new memory is allocated.
  * If pBuffer!=NULL memory of given buffer is reused.
@@ -334,16 +321,15 @@ FGFlightRecorder::capture(double SimTime, FGReplayData* pRecycledBuffer)
 {
     if (!pRecycledBuffer)
     {
-        pRecycledBuffer = createEmptyRecord();
+        pRecycledBuffer = new FGReplayData;
         if (!pRecycledBuffer)
             return NULL;
     }
-    unsigned char* pBuffer = (unsigned char*) pRecycledBuffer;
-
     int Offset = 0;
     pRecycledBuffer->sim_time = SimTime;
-    Offset += sizeof(double);
-
+    pRecycledBuffer->raw_data.resize( m_TotalRecordSize);
+    char* pBuffer = &pRecycledBuffer->raw_data.front();
+    
     // 64bit aligned data first!
     {
         // capture doubles
@@ -417,9 +403,26 @@ FGFlightRecorder::capture(double SimTime, FGReplayData* pRecycledBuffer)
         }
     }
 
-    assert(Offset == m_TotalRecordSize);
+    assert(Offset + sizeof(double) == m_TotalRecordSize);
+    
+    // If m_ReplayMultiplayer is true, move all recent
+    // multiplayer messages from m_MultiplayMgr into
+    // pRecycledBuffer->multiplayer_messages. Otherwise clear m_MultiplayMgr's
+    // list of recent messages.
+    //
+    pRecycledBuffer->multiplayer_messages.clear();
+    bool    replayMultiplayer = m_ReplayMultiplayer->getBoolValue();
+    for(;;) {
+        auto MultiplayerMessage = m_MultiplayMgr->popMessageHistory();
+        if (!MultiplayerMessage) {
+            break;
+        }
+        if (replayMultiplayer) {
+            pRecycledBuffer->multiplayer_messages.push_back( MultiplayerMessage);
+        }
+    }
 
-    return (FGReplayData*) pBuffer;
+    return pRecycledBuffer;
 }
 
 /** Do interpolation as defined by given interpolation type and weighting ratio. */
@@ -465,8 +468,8 @@ weighting(TInterpolation interpolation, double ratio, double v1,double v2)
 void
 FGFlightRecorder::replay(double SimTime, const FGReplayData* _pNextBuffer, const FGReplayData* _pLastBuffer)
 {
-    const char* pLastBuffer = (const char*) _pLastBuffer;
-    const char* pBuffer = (const char*) _pNextBuffer;
+    const char* pLastBuffer = (_pLastBuffer) ? &_pLastBuffer->raw_data.front() : nullptr;
+    const char* pBuffer = (_pNextBuffer) ? &_pNextBuffer->raw_data.front() : nullptr;
     if (!pBuffer)
         return;
 
@@ -487,8 +490,6 @@ FGFlightRecorder::replay(double SimTime, const FGReplayData* _pNextBuffer, const
         }
     }
 
-    Offset += sizeof(double);
-
     // 64bit aligned data first!
     {
         // restore doubles
@@ -573,6 +574,11 @@ FGFlightRecorder::replay(double SimTime, const FGReplayData* _pNextBuffer, const
             m_CaptureBool[i].Signal->setBoolValue(0 != (pFlags[i>>3] & (1 << (i&7))));
         }
     }
+    
+    // Replay any multiplayer messages.
+    for ( auto multiplayer_message: _pNextBuffer->multiplayer_messages) {
+        m_MultiplayMgr->pushMessageHistory(multiplayer_message);
+    }
 }
 
 int
diff --git a/src/Aircraft/flightrecorder.hxx b/src/Aircraft/flightrecorder.hxx
index f3759df64..27270e7e6 100644
--- a/src/Aircraft/flightrecorder.hxx
+++ b/src/Aircraft/flightrecorder.hxx
@@ -24,6 +24,7 @@
 #define FLIGHTRECORDER_HXX_
 
 #include <simgear/props/props.hxx>
+#include <MultiPlayer/multiplaymgr.hxx>
 #include "replay.hxx"
 
 namespace FlightRecorder
@@ -55,11 +56,9 @@ public:
 
     void            reinit              (void);
     void            reinit              (SGPropertyNode_ptr ConfigNode);
-    FGReplayData*   createEmptyRecord   (void);
     FGReplayData*   capture             (double SimTime, FGReplayData* pRecycledBuffer);
     void            replay              (double SimTime, const FGReplayData* pNextBuffer,
                                          const FGReplayData* pLastBuffer = NULL);
-    void            deleteRecord        (FGReplayData* pRecord);
 
     int             getRecordSize       (void) { return m_TotalRecordSize;}
     void            getConfig           (SGPropertyNode* root);
@@ -78,6 +77,7 @@ private:
 
     SGPropertyNode_ptr m_RecorderNode;
     SGPropertyNode_ptr m_ConfigNode;
+    SGPropertyNode_ptr m_ReplayMultiplayer;
 
     FlightRecorder::TSignalList m_CaptureDouble;
     FlightRecorder::TSignalList m_CaptureFloat;
@@ -89,6 +89,7 @@ private:
     int m_TotalRecordSize;
     std::string m_ConfigName;
     bool m_usingDefaultConfig;
+    FGMultiplayMgr* m_MultiplayMgr;
 };
 
 #endif /* FLIGHTRECORDER_HXX_ */
diff --git a/src/Aircraft/replay.cxx b/src/Aircraft/replay.cxx
index 504929b9b..a7bee672a 100644
--- a/src/Aircraft/replay.cxx
+++ b/src/Aircraft/replay.cxx
@@ -109,22 +109,22 @@ FGReplay::clear()
 {
     while ( !short_term.empty() )
     {
-        m_pRecorder->deleteRecord(short_term.front());
+        delete short_term.front();
         short_term.pop_front();
     }
     while ( !medium_term.empty() )
     {
-        m_pRecorder->deleteRecord(medium_term.front());
+        delete medium_term.front();
         medium_term.pop_front();
     }
     while ( !long_term.empty() )
     {
-        m_pRecorder->deleteRecord(long_term.front());
+        delete long_term.front();
         long_term.pop_front();
     }
     while ( !recycler.empty() )
     {
-        m_pRecorder->deleteRecord(recycler.front());
+        delete recycler.front();
         recycler.pop_front();
     }
 
@@ -213,7 +213,7 @@ FGReplay::fillRecycler()
                              (m_low_res_time*m_long_sample_rate));
     for (int i = 0; i < estNrObjects; i++)
     {
-        FGReplayData* r = m_pRecorder->createEmptyRecord();
+        FGReplayData* r = new FGReplayData;
         if (r)
             recycler.push_back(r);
         else
@@ -753,7 +753,9 @@ saveRawReplayData(gzContainerWriter& output, const replay_list_type& ReplayData,
            !output.fail())
     {
         const FGReplayData* pRecord = *it++;
-        output.write((char*)pRecord, RecordSize);
+        assert(RecordSize == pRecord->raw_data.size());
+        output.write(reinterpret_cast<const char*>(&pRecord->sim_time), sizeof(pRecord->sim_time));
+        output.write(&pRecord->raw_data.front(), pRecord->raw_data.size());
         CheckCount++;
     }
 
@@ -796,8 +798,10 @@ loadRawReplayData(gzContainerReader& input, FGFlightRecorder* pRecorder, replay_
     size_t CheckCount = 0;
     for (CheckCount=0; (CheckCount<Count)&&(!input.eof()); ++CheckCount)
     {
-        FGReplayData* pBuffer = pRecorder->createEmptyRecord();
-        input.read((char*) pBuffer, RecordSize);
+        FGReplayData* pBuffer = new FGReplayData;
+        input.read(reinterpret_cast<char*>(&pBuffer->sim_time), sizeof(pBuffer->sim_time));
+        pBuffer->raw_data.resize(RecordSize);
+        input.read(&pBuffer->raw_data.front(), RecordSize);
         ReplayData.push_back(pBuffer);
     }
 
diff --git a/src/Aircraft/replay.hxx b/src/Aircraft/replay.hxx
index 7377ef7ab..c8e89ef27 100644
--- a/src/Aircraft/replay.hxx
+++ b/src/Aircraft/replay.hxx
@@ -40,9 +40,13 @@
 class FGFlightRecorder;
 
 typedef struct {
+
     double sim_time;
-    char   raw_data;
-    /* more data here, hidden to the outside world */
+    // Our aircraft state.
+    std::vector<char>   raw_data;
+    
+    // Incoming multiplayer messages.
+    std::vector<std::shared_ptr<std::vector<char>>> multiplayer_messages;
 } FGReplayData;
 
 typedef struct {
diff --git a/src/MultiPlayer/multiplaymgr.cxx b/src/MultiPlayer/multiplaymgr.cxx
index 7376d914d..1eea91d70 100644
--- a/src/MultiPlayer/multiplaymgr.cxx
+++ b/src/MultiPlayer/multiplaymgr.cxx
@@ -963,6 +963,7 @@ FGMultiplayMgr::FGMultiplayMgr()
   pMultiPlayTransmitPropertyBase = fgGetNode("/sim/multiplay/transmit-filter-property-base", true);
   pMultiPlayRange = fgGetNode("/sim/multiplay/visibility-range-nm", true);
   pMultiPlayRange->setIntValue(100);
+  pReplayState = fgGetNode("/sim/replay/replay-state", true);
 } // FGMultiplayMgr::FGMultiplayMgr()
 //////////////////////////////////////////////////////////////////////
 
@@ -1692,6 +1693,127 @@ FGMultiplayMgr::SendTextMessage(const string &MsgText)
 } // FGMultiplayMgr::SendTextMessage ()
 //////////////////////////////////////////////////////////////////////
 
+
+// If a message is available from mSocket, copies into <msgBuf>, converts
+// endiness of the T_MsgHdr, and returns length.
+//
+// Otherwise returns 0.
+//
+int FGMultiplayMgr::GetMsgNetwork(MsgBuf& msgBuf, simgear::IPAddress& SenderAddress)
+{
+        //////////////////////////////////////////////////
+        //  Although the recv call asks for
+        //  MAX_PACKET_SIZE of data, the number of bytes
+        //  returned will only be that of the next
+        //  packet waiting to be processed.
+        //////////////////////////////////////////////////
+        int RecvStatus = mSocket->recvfrom(msgBuf.Msg, sizeof(msgBuf.Msg), 0,
+                                  &SenderAddress);
+        //////////////////////////////////////////////////
+        //  no Data received
+        //////////////////////////////////////////////////
+        if (RecvStatus == 0)
+            return 0;
+
+        // socket error reported?
+        // errno isn't thread-safe - so only check its value when
+        // socket return status < 0 really indicates a failure.
+        if ((RecvStatus < 0)&&
+            ((errno == EAGAIN) || (errno == 0))) // MSVC output "NoError" otherwise
+        {
+            // ignore "normal" errors
+            return 0;
+        }
+
+        if (RecvStatus<0)
+        {
+    #ifdef _WIN32
+            if (::WSAGetLastError() != WSAEWOULDBLOCK) // this is normal on a receive when there is no data
+            {
+                // with Winsock the error will not be the actual problem.
+                SG_LOG(SG_NETWORK, SG_INFO, "FGMultiplayMgr::MP_ProcessData - Unable to receive data. WSAGetLastError=" << ::WSAGetLastError());
+            }
+    #else
+            SG_LOG(SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - Unable to receive data. "
+                << strerror(errno) << "(errno " << errno << ")");
+    #endif
+            return 0;
+        }
+        
+        T_MsgHdr* MsgHdr = msgBuf.msgHdr();
+        MsgHdr->Magic       = XDR_decode_uint32 (MsgHdr->Magic);
+        MsgHdr->Version     = XDR_decode_uint32 (MsgHdr->Version);
+        MsgHdr->MsgId       = XDR_decode_uint32 (MsgHdr->MsgId);
+        MsgHdr->MsgLen      = XDR_decode_uint32 (MsgHdr->MsgLen);
+        MsgHdr->ReplyPort   = XDR_decode_uint32 (MsgHdr->ReplyPort);
+        MsgHdr->Callsign[MAX_CALLSIGN_LEN -1] = '\0';
+        
+        return RecvStatus;
+}
+
+// Returns message in msgBuf out-param.
+//
+// If we are in replay mode, we return recorded messages (omitting recorded
+// chat messages), and live chat messages from mSocket.
+//
+int FGMultiplayMgr::GetMsg(MsgBuf& msgBuf, simgear::IPAddress& SenderAddress)
+{
+    if (pReplayState->getIntValue()) {
+        // We are replaying, so return non-chat multiplayer messages from
+        // mReplayMessageQueue and live chat messages from mSocket.
+        //
+        for(;;) {
+        
+            if (mReplayMessageQueue.empty()) {
+                // No recorded messages available, so look for chat messages
+                // only from <mSocket>.
+                //
+                int RecvStatus = GetMsgNetwork(msgBuf, SenderAddress);
+                if (RecvStatus == 0) {
+                    // No recorded messages, and no live messages, so return 0.
+                    return 0;
+                }
+                if (msgBuf.Header.MsgId == CHAT_MSG_ID) {
+                    return RecvStatus;
+                }
+                // If we get here, there is a live message but it is a
+                // multiplayer aircraft position so we ignore it while
+                // replaying.
+                //
+            }
+            else {
+                // Replay recorded message, unless it is a chat message.
+                //
+                std::shared_ptr<std::vector<char>> replayMessage = mReplayMessageQueue.front();
+                mReplayMessageQueue.pop_front();
+                assert(replayMessage->size() <= sizeof(msgBuf));
+                int length = replayMessage->size();
+                memcpy(&msgBuf.Msg, &replayMessage->front(), length);
+                // Don't return chat messages.
+                if (msgBuf.Header.MsgId != CHAT_MSG_ID) {
+                    SG_LOG(SG_NETWORK, SG_INFO,
+                            "replaying message length=" << replayMessage->size()
+                            << ". num remaining messages=" << mReplayMessageQueue.size()
+                            );
+                    return length;
+                }
+            }
+        }
+    }
+    else {
+        int length = GetMsgNetwork(msgBuf, SenderAddress);
+        
+        // Make raw incoming packet available to recording code.
+        if (length) {
+            std::shared_ptr<std::vector<char>> data( new std::vector<char>(length));
+            memcpy( &data->front(), msgBuf.Msg, length);
+            mRecordMessageQueue.push_back(data);
+        }
+        return length;
+    }
+}
+
+
 //////////////////////////////////////////////////////////////////////
 //
 //  Name: ProcessData
@@ -1721,47 +1843,12 @@ FGMultiplayMgr::update(double dt)
   //////////////////////////////////////////////////
   ssize_t bytes;
   do {
-    MsgBuf msgBuf;
-    //////////////////////////////////////////////////
-    //  Although the recv call asks for
-    //  MAX_PACKET_SIZE of data, the number of bytes
-    //  returned will only be that of the next
-    //  packet waiting to be processed.
-    //////////////////////////////////////////////////
+    MsgBuf  msgBuf;
     simgear::IPAddress SenderAddress;
-    int RecvStatus = mSocket->recvfrom(msgBuf.Msg, sizeof(msgBuf.Msg), 0,
-                              &SenderAddress);
-    //////////////////////////////////////////////////
-    //  no Data received
-    //////////////////////////////////////////////////
-    if (RecvStatus == 0)
-        break;
-
-    // socket error reported?
-    // errno isn't thread-safe - so only check its value when
-    // socket return status < 0 really indicates a failure.
-    if ((RecvStatus < 0)&&
-        ((errno == EAGAIN) || (errno == 0))) // MSVC output "NoError" otherwise
-    {
-        // ignore "normal" errors
+    int RecvStatus = GetMsg(msgBuf, SenderAddress);
+    if (RecvStatus == 0) {
         break;
     }
-
-    if (RecvStatus<0)
-    {
-#ifdef _WIN32
-        if (::WSAGetLastError() != WSAEWOULDBLOCK) // this is normal on a receive when there is no data
-        {
-            // with Winsock the error will not be the actual problem.
-            SG_LOG(SG_NETWORK, SG_INFO, "FGMultiplayMgr::MP_ProcessData - Unable to receive data. WSAGetLastError=" << ::WSAGetLastError());
-        }
-#else
-        SG_LOG(SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - Unable to receive data. "
-            << strerror(errno) << "(errno " << errno << ")");
-#endif
-        break;
-    }
-
     // status is positive: bytes received
     bytes = (ssize_t) RecvStatus;
     if (bytes <= static_cast<ssize_t>(sizeof(T_MsgHdr))) {
@@ -1769,16 +1856,11 @@ FGMultiplayMgr::update(double dt)
               << "received message with insufficient data" );
       break;
     }
+    
     //////////////////////////////////////////////////
     //  Read header
     //////////////////////////////////////////////////
     T_MsgHdr* MsgHdr = msgBuf.msgHdr();
-    MsgHdr->Magic       = XDR_decode_uint32 (MsgHdr->Magic);
-    MsgHdr->Version     = XDR_decode_uint32 (MsgHdr->Version);
-    MsgHdr->MsgId       = XDR_decode_uint32 (MsgHdr->MsgId);
-    MsgHdr->MsgLen      = XDR_decode_uint32 (MsgHdr->MsgLen);
-    MsgHdr->ReplyPort   = XDR_decode_uint32 (MsgHdr->ReplyPort);
-    MsgHdr->Callsign[MAX_CALLSIGN_LEN -1] = '\0';
     if (MsgHdr->Magic != MSG_MAGIC) {
         SG_LOG(SG_NETWORK, SG_INFO, "FGMultiplayMgr::MP_ProcessData - "
               << "message has invalid magic number!" );
@@ -2265,6 +2347,24 @@ FGMultiplayMgr::ProcessPosMsg(const FGMultiplayMgr::MsgBuf& Msg,
     mp = addMultiplayer(MsgHdr->Callsign, PosMsg->Model, fallback_model_index);
   mp->addMotionInfo(motionInfo, stamp);
 } // FGMultiplayMgr::ProcessPosMsg()
+
+
+std::shared_ptr<std::vector<char>> FGMultiplayMgr::popMessageHistory()
+{
+    if (mRecordMessageQueue.empty()) {
+        return nullptr;
+    }
+
+    std::shared_ptr<std::vector<char>> ret = mRecordMessageQueue.front();
+    mRecordMessageQueue.pop_front();
+    return ret;
+}
+
+void FGMultiplayMgr::pushMessageHistory(std::shared_ptr<std::vector<char>> message)
+{
+    mReplayMessageQueue.push_back(message);
+}
+
 //////////////////////////////////////////////////////////////////////
 
 //////////////////////////////////////////////////////////////////////
diff --git a/src/MultiPlayer/multiplaymgr.hxx b/src/MultiPlayer/multiplaymgr.hxx
index 13a6a4523..379706a6f 100644
--- a/src/MultiPlayer/multiplaymgr.hxx
+++ b/src/MultiPlayer/multiplaymgr.hxx
@@ -34,6 +34,7 @@
 const int MIN_MP_PROTOCOL_VERSION = 1;
 const int MAX_MP_PROTOCOL_VERSION = 2;
 
+#include <deque>
 #include <string>
 #include <vector>
 #include <memory>
@@ -69,6 +70,10 @@ public:
     // receiver
 
   FGAIMultiplayer* getMultiplayer(const std::string& callsign);
+
+    std::shared_ptr<vector<char>> popMessageHistory();
+    void pushMessageHistory(std::shared_ptr<vector<char>> message);
+
 private:
     friend class MPPropertyListener;
 
@@ -100,6 +105,8 @@ private:
                        long stamp);
     void ProcessChatMsg(const MsgBuf& Msg, const simgear::IPAddress& SenderAddress);
     bool isSane(const FGExternalMotionData& motionInfo);
+    int GetMsgNetwork(MsgBuf& msgBuf, simgear::IPAddress& SenderAddress);
+    int GetMsg(MsgBuf& msgBuf, simgear::IPAddress& SenderAddress);
 
     /// maps from the callsign string to the FGAIMultiplayer
     typedef std::map<std::string, SGSharedPtr<FGAIMultiplayer> > MultiPlayerMap;
@@ -120,6 +127,7 @@ private:
     SGPropertyNode *pMultiPlayDebugLevel;
     SGPropertyNode *pMultiPlayRange;
     SGPropertyNode *pMultiPlayTransmitPropertyBase;
+    SGPropertyNode *pReplayState;
 
     typedef std::map<unsigned int, const struct IdPropertyList*> PropertyDefinitionMap;
     PropertyDefinitionMap mPropertyDefinition;
@@ -130,6 +138,9 @@ private:
 
     double mDt; // reciprocal of /sim/multiplay/tx-rate-hz
     double mTimeUntilSend;
+    
+    std::deque<std::shared_ptr<std::vector<char>>>  mRecordMessageQueue;
+    std::deque<std::shared_ptr<std::vector<char>>>  mReplayMessageQueue;
 };
 
 #endif