Multiplayer replay: new, support for replaying multiplayer aircraft as well as the user's aircraft.
At the moment this only works for live replays - we don't (yet) write the multiplayer information to fgtape files. Enable with: --prop:bool:/sim/replay/multiplayer=true This works by copying all raw multiplayer packets into a buffer in FGMultiplayMgr. Each time it is called, FGFlightRecorder::capture() moves all the available packet from this buffer into its FGReplayData. Thus we store roughly syncronised multiplayer packets along with the user aircraft's detailed replay properties. When replaying, FGFlightRecorder pushes packets into a buffer in FGMultiplayMgr, which are used instead of live multiplayer packets. [Actually when replaying FGMultiplayMgr still reads live packets in order to handle live chat messages, and ignores chat messages from FGFlightRecorder.]
This commit is contained in:
parent
0015744486
commit
e7b1f3f52e
6 changed files with 211 additions and 85 deletions
|
@ -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
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue