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