//////////////////////////////////////////////////////////////////////
//
-// multiplaymgr.hpp
+// multiplaymgr.cxx
//
// Written by Duncan McCreanor, started February 2003.
// duncan.mccreanor@airservicesaustralia.com
#include <algorithm>
#include <cstring>
#include <osg/Math> // isNaN
-#include <plib/netSocket.h>
#include <simgear/misc/stdint.hxx>
#include <simgear/timing/timestamp.hxx>
// Initialise object
//
//////////////////////////////////////////////////////////////////////
-bool
+void
FGMultiplayMgr::init (void)
{
//////////////////////////////////////////////////
//////////////////////////////////////////////////
if (mInitialised) {
SG_LOG(SG_NETWORK, SG_WARN, "FGMultiplayMgr::init - already initialised");
- return false;
+ return;
}
//////////////////////////////////////////////////
// Set members from property values
if (rxPort <= 0) {
SG_LOG(SG_NETWORK, SG_DEBUG,
"FGMultiplayMgr - No receiver port, Multiplayermode disabled");
- return (false);
+ return;
}
if (mCallsign.empty())
mCallsign = "JohnDoe"; // FIXME: use getpwuid
SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-callsign= "<<mCallsign);
Close(); // Should Init be called twice, close Socket first
// A memory leak was reported here by valgrind
- mSocket = new netSocket();
+ mSocket = new simgear::Socket();
if (!mSocket->open(false)) {
SG_LOG( SG_NETWORK, SG_DEBUG,
"FGMultiplayMgr::init - Failed to create data socket" );
- return false;
+ return;
}
mSocket->setBlocking(false);
if (mSocket->bind(rxAddress.c_str(), rxPort) != 0) {
perror("bind");
SG_LOG( SG_NETWORK, SG_DEBUG,
"FGMultiplayMgr::Open - Failed to bind receive socket" );
- return false;
+ return;
}
+
mInitialised = true;
- return true;
} // FGMultiplayMgr::init()
//////////////////////////////////////////////////////////////////////
T_MsgHdr Header;
};
+bool
+FGMultiplayMgr::isSane(const FGExternalMotionData& motionInfo)
+{
+ // check for corrupted data (NaNs)
+ bool isCorrupted = false;
+ isCorrupted |= ((osg::isNaN(motionInfo.time )) ||
+ (osg::isNaN(motionInfo.lag )) ||
+ (osg::isNaN(motionInfo.orientation(3) )));
+ for (unsigned i = 0; (i < 3)&&(!isCorrupted); ++i)
+ {
+ isCorrupted |= ((osg::isNaN(motionInfo.position(i) ))||
+ (osg::isNaN(motionInfo.orientation(i) ))||
+ (osg::isNaN(motionInfo.linearVel(i)) )||
+ (osg::isNaN(motionInfo.angularVel(i)) )||
+ (osg::isNaN(motionInfo.linearAccel(i)) )||
+ (osg::isNaN(motionInfo.angularAccel(i)) ));
+ }
+ return !isCorrupted;
+}
+
void
FGMultiplayMgr::SendMyPosition(const FGExternalMotionData& motionInfo)
{
if ((! mInitialised) || (! mHaveServer))
return;
+
if (! mHaveServer) {
- SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::SendMyPosition - no server");
- return;
+ SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::SendMyPosition - no server");
+ return;
}
- MsgBuf msgBuf;
+ if (!isSane(motionInfo))
+ {
+ // Current local data is invalid (NaN), so stop MP transmission.
+ // => Be nice to older FG versions (no NaN checks) and don't waste bandwidth.
+ SG_LOG(SG_NETWORK, SG_ALERT, "FGMultiplayMgr::SendMyPosition - "
+ << "Local data is invalid (NaN). Data not transmitted.");
+ return;
+ }
+
+ static MsgBuf msgBuf;
+ static unsigned msgLen = 0;
T_PositionMsg* PosMsg = msgBuf.posMsg();
strncpy(PosMsg->Model, fgGetString("/sim/model/path"), MAX_MODEL_NAME_LEN);
PosMsg->Model[MAX_MODEL_NAME_LEN - 1] = '\0';
-
- PosMsg->time = XDR_encode_double (motionInfo.time);
- PosMsg->lag = XDR_encode_double (motionInfo.lag);
- for (unsigned i = 0 ; i < 3; ++i)
- PosMsg->position[i] = XDR_encode_double (motionInfo.position(i));
- SGVec3f angleAxis;
- motionInfo.orientation.getAngleAxis(angleAxis);
- for (unsigned i = 0 ; i < 3; ++i)
- PosMsg->orientation[i] = XDR_encode_float (angleAxis(i));
- for (unsigned i = 0 ; i < 3; ++i)
- PosMsg->linearVel[i] = XDR_encode_float (motionInfo.linearVel(i));
- for (unsigned i = 0 ; i < 3; ++i)
- PosMsg->angularVel[i] = XDR_encode_float (motionInfo.angularVel(i));
- for (unsigned i = 0 ; i < 3; ++i)
- PosMsg->linearAccel[i] = XDR_encode_float (motionInfo.linearAccel(i));
- for (unsigned i = 0 ; i < 3; ++i)
- PosMsg->angularAccel[i] = XDR_encode_float (motionInfo.angularAccel(i));
-
- xdr_data_t* ptr = msgBuf.properties();
- std::vector<FGPropertyData*>::const_iterator it;
- it = motionInfo.properties.begin();
- //cout << "OUTPUT PROPERTIES\n";
- xdr_data_t* msgEnd = msgBuf.propsEnd();
- while (it != motionInfo.properties.end() && ptr + 2 < msgEnd) {
-
- // First element is the ID. Write it out when we know we have room for
- // the whole property.
- xdr_data_t id = XDR_encode_uint32((*it)->id);
- // The actual data representation depends on the type
- switch ((*it)->type) {
- case simgear::props::INT:
- case simgear::props::BOOL:
- case simgear::props::LONG:
- *ptr++ = id;
- *ptr++ = XDR_encode_uint32((*it)->int_value);
- //cout << "Prop:" << (*it)->id << " " << (*it)->type << " "<< (*it)->int_value << "\n";
- break;
- case simgear::props::FLOAT:
- case simgear::props::DOUBLE:
- *ptr++ = id;
- *ptr++ = XDR_encode_float((*it)->float_value);
- //cout << "Prop:" << (*it)->id << " " << (*it)->type << " "<< (*it)->float_value << "\n";
- break;
- case simgear::props::STRING:
- case simgear::props::UNSPECIFIED:
- {
- // String is complicated. It consists of
- // The length of the string
- // The string itself
- // Padding to the nearest 4-bytes.
- const char* lcharptr = (*it)->string_value;
-
- if (lcharptr != 0)
- {
- // Add the length
- ////cout << "String length: " << strlen(lcharptr) << "\n";
- uint32_t len = strlen(lcharptr);
- if (len > MAX_TEXT_SIZE)
- len = MAX_TEXT_SIZE;
- // XXX This should not be using 4 bytes per character!
- // If there's not enough room for this property, drop it
- // on the floor.
- if (ptr + 2 + ((len + 3) & ~3) > msgEnd)
- goto escape;
- //cout << "String length unint32: " << len << "\n";
+ if (fgGetBool("/sim/freeze/replay-state", true))
+ {
+ // do not send position updates during replay
+ for (unsigned i = 0 ; i < 3; ++i)
+ {
+ // no movement during replay
+ PosMsg->linearVel[i] = XDR_encode_float (0.0);
+ PosMsg->angularVel[i] = XDR_encode_float (0.0);
+ PosMsg->linearAccel[i] = XDR_encode_float (0.0);
+ PosMsg->angularAccel[i] = XDR_encode_float (0.0);
+ }
+ // all other data remains unchanged (resend last state)
+ }
+ else
+ {
+ PosMsg->time = XDR_encode_double (motionInfo.time);
+ PosMsg->lag = XDR_encode_double (motionInfo.lag);
+ for (unsigned i = 0 ; i < 3; ++i)
+ PosMsg->position[i] = XDR_encode_double (motionInfo.position(i));
+ SGVec3f angleAxis;
+ motionInfo.orientation.getAngleAxis(angleAxis);
+ for (unsigned i = 0 ; i < 3; ++i)
+ PosMsg->orientation[i] = XDR_encode_float (angleAxis(i));
+ for (unsigned i = 0 ; i < 3; ++i)
+ PosMsg->linearVel[i] = XDR_encode_float (motionInfo.linearVel(i));
+ for (unsigned i = 0 ; i < 3; ++i)
+ PosMsg->angularVel[i] = XDR_encode_float (motionInfo.angularVel(i));
+ for (unsigned i = 0 ; i < 3; ++i)
+ PosMsg->linearAccel[i] = XDR_encode_float (motionInfo.linearAccel(i));
+ for (unsigned i = 0 ; i < 3; ++i)
+ PosMsg->angularAccel[i] = XDR_encode_float (motionInfo.angularAccel(i));
+
+ xdr_data_t* ptr = msgBuf.properties();
+ std::vector<FGPropertyData*>::const_iterator it;
+ it = motionInfo.properties.begin();
+ //cout << "OUTPUT PROPERTIES\n";
+ xdr_data_t* msgEnd = msgBuf.propsEnd();
+ while (it != motionInfo.properties.end() && ptr + 2 < msgEnd) {
+
+ // First element is the ID. Write it out when we know we have room for
+ // the whole property.
+ xdr_data_t id = XDR_encode_uint32((*it)->id);
+ // The actual data representation depends on the type
+ switch ((*it)->type) {
+ case simgear::props::INT:
+ case simgear::props::BOOL:
+ case simgear::props::LONG:
*ptr++ = id;
- *ptr++ = XDR_encode_uint32(len);
- if (len != 0)
+ *ptr++ = XDR_encode_uint32((*it)->int_value);
+ //cout << "Prop:" << (*it)->id << " " << (*it)->type << " "<< (*it)->int_value << "\n";
+ break;
+ case simgear::props::FLOAT:
+ case simgear::props::DOUBLE:
+ *ptr++ = id;
+ *ptr++ = XDR_encode_float((*it)->float_value);
+ //cout << "Prop:" << (*it)->id << " " << (*it)->type << " "<< (*it)->float_value << "\n";
+ break;
+ case simgear::props::STRING:
+ case simgear::props::UNSPECIFIED:
{
- // Now the text itself
- // XXX This should not be using 4 bytes per character!
- int lcount = 0;
- while ((*lcharptr != '\0') && (lcount < MAX_TEXT_SIZE))
+ // String is complicated. It consists of
+ // The length of the string
+ // The string itself
+ // Padding to the nearest 4-bytes.
+ const char* lcharptr = (*it)->string_value;
+
+ if (lcharptr != 0)
{
- *ptr++ = XDR_encode_int8(*lcharptr);
- lcharptr++;
- lcount++;
+ // Add the length
+ ////cout << "String length: " << strlen(lcharptr) << "\n";
+ uint32_t len = strlen(lcharptr);
+ if (len > MAX_TEXT_SIZE)
+ len = MAX_TEXT_SIZE;
+ // XXX This should not be using 4 bytes per character!
+ // If there's not enough room for this property, drop it
+ // on the floor.
+ if (ptr + 2 + ((len + 3) & ~3) > msgEnd)
+ goto escape;
+ //cout << "String length unint32: " << len << "\n";
+ *ptr++ = id;
+ *ptr++ = XDR_encode_uint32(len);
+ if (len != 0)
+ {
+ // Now the text itself
+ // XXX This should not be using 4 bytes per character!
+ int lcount = 0;
+ while ((*lcharptr != '\0') && (lcount < MAX_TEXT_SIZE))
+ {
+ *ptr++ = XDR_encode_int8(*lcharptr);
+ lcharptr++;
+ lcount++;
+ }
+
+ //cout << "Prop:" << (*it)->id << " " << (*it)->type << " " << len << " " << (*it)->string_value;
+
+ // Now pad if required
+ while ((lcount % 4) != 0)
+ {
+ *ptr++ = XDR_encode_int8(0);
+ lcount++;
+ //cout << "0";
+ }
+
+ //cout << "\n";
+ }
}
-
- //cout << "Prop:" << (*it)->id << " " << (*it)->type << " " << len << " " << (*it)->string_value;
-
- // Now pad if required
- while ((lcount % 4) != 0)
+ else
{
- *ptr++ = XDR_encode_int8(0);
- lcount++;
- //cout << "0";
+ // Nothing to encode
+ *ptr++ = id;
+ *ptr++ = XDR_encode_uint32(0);
+ //cout << "Prop:" << (*it)->id << " " << (*it)->type << " 0\n";
}
-
- //cout << "\n";
}
- }
- else
- {
- // Nothing to encode
+ break;
+
+ default:
+ //cout << " Unknown Type: " << (*it)->type << "\n";
*ptr++ = id;
- *ptr++ = XDR_encode_uint32(0);
- //cout << "Prop:" << (*it)->id << " " << (*it)->type << " 0\n";
- }
+ *ptr++ = XDR_encode_float((*it)->float_value);;
+ //cout << "Prop:" << (*it)->id << " " << (*it)->type << " "<< (*it)->float_value << "\n";
+ break;
}
- break;
-
- default:
- //cout << " Unknown Type: " << (*it)->type << "\n";
- *ptr++ = id;
- *ptr++ = XDR_encode_float((*it)->float_value);;
- //cout << "Prop:" << (*it)->id << " " << (*it)->type << " "<< (*it)->float_value << "\n";
- break;
- }
-
- ++it;
+
+ ++it;
+ }
+ escape:
+ msgLen = reinterpret_cast<char*>(ptr) - msgBuf.Msg;
+ FillMsgHdr(msgBuf.msgHdr(), POS_DATA_ID, msgLen);
}
-escape:
- unsigned msgLen = reinterpret_cast<char*>(ptr) - msgBuf.Msg;
- FillMsgHdr(msgBuf.msgHdr(), POS_DATA_ID, msgLen);
- mSocket->sendto(msgBuf.Msg, msgLen, 0, &mServer);
+ if (msgLen>0)
+ mSocket->sendto(msgBuf.Msg, msgLen, 0, &mServer);
SG_LOG(SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::SendMyPosition");
} // FGMultiplayMgr::SendMyPosition()
//
//////////////////////////////////////////////////////////////////////
void
-FGMultiplayMgr::Update(void)
+FGMultiplayMgr::update(double)
{
if (!mInitialised)
return;
// returned will only be that of the next
// packet waiting to be processed.
//////////////////////////////////////////////////
- netAddress SenderAddress;
+ simgear::IPAddress SenderAddress;
bytes = mSocket->recvfrom(msgBuf.Msg, sizeof(msgBuf.Msg), 0,
&SenderAddress);
//////////////////////////////////////////////////
}
if (MsgHdr->Version != PROTO_VER) {
SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - "
- << "message has invalid protocoll number!" );
+ << "message has invalid protocol number!" );
break;
}
if (MsgHdr->MsgLen != bytes) {
//////////////////////////////////////////////////////////////////////
void
FGMultiplayMgr::ProcessPosMsg(const FGMultiplayMgr::MsgBuf& Msg,
- const netAddress& SenderAddress, long stamp)
+ const simgear::IPAddress& SenderAddress, long stamp)
{
const T_MsgHdr* MsgHdr = Msg.msgHdr();
if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + sizeof(T_PositionMsg)) {
for (unsigned i = 0; i < 3; ++i)
motionInfo.angularAccel(i) = XDR_decode_float(PosMsg->angularAccel[i]);
+ // sanity check: do not allow injection of corrupted data (NaNs)
+ if (!isSane(motionInfo))
+ {
+ // drop this message, keep old position until receiving valid data
+ SG_LOG(SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::ProcessPosMsg - "
+ << "Position message with invalid data (NaN) received from "
+ << MsgHdr->Callsign);
+ return;
+ }
//cout << "INPUT MESSAGE\n";
goto noprops;
}
while (xdr < Msg.propsRecvdEnd()) {
- FGPropertyData* pData = new FGPropertyData;
// simgear::props::Type type = simgear::props::UNSPECIFIED;
// First element is always the ID
- pData->id = XDR_decode_uint32(*xdr);
+ unsigned id = XDR_decode_uint32(*xdr);
//cout << pData->id << " ";
xdr++;
// Check the ID actually exists and get the type
- const IdPropertyList* plist = findProperty(pData->id);
+ const IdPropertyList* plist = findProperty(id);
if (plist)
{
+ FGPropertyData* pData = new FGPropertyData;
+ pData->id = id;
pData->type = plist->type;
// How we decode the remainder of the property depends on the type
switch (pData->type) {
SG_LOG(SG_NETWORK, SG_DEBUG, "Unknown Prop type " << pData->id << " " << pData->type);
xdr++;
break;
- }
+ }
motionInfo.properties.push_back(pData);
}
// We failed to find the property. We'll try the next packet immediately.
SG_LOG(SG_NETWORK, SG_INFO, "FGMultiplayMgr::ProcessPosMsg - "
"message from " << MsgHdr->Callsign << " has unknown property id "
- << pData->id);
+ << id);
}
}
noprops:
//////////////////////////////////////////////////////////////////////
//
// handle a chat message
-// FIXME: display chat message withi flightgear
+// FIXME: display chat message within flightgear
//
//////////////////////////////////////////////////////////////////////
void
FGMultiplayMgr::ProcessChatMsg(const MsgBuf& Msg,
- const netAddress& SenderAddress)
+ const simgear::IPAddress& SenderAddress)
{
const T_MsgHdr* MsgHdr = Msg.msgHdr();
if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + 1) {