]> git.mxchange.org Git - flightgear.git/blobdiff - src/MultiPlayer/multiplaymgr.cxx
Merge branch 'attenuation' into navaids-radio
[flightgear.git] / src / MultiPlayer / multiplaymgr.cxx
index 5fc5c2efbb977077444d9218fccf0e8042f2f7df..123317c63536b25d5d9893f8fd13a41a7e09055c 100644 (file)
@@ -1,6 +1,6 @@
 //////////////////////////////////////////////////////////////////////
 //
-// multiplaymgr.hpp
+// multiplaymgr.cxx
 //
 // Written by Duncan McCreanor, started February 2003.
 // duncan.mccreanor@airservicesaustralia.com
@@ -34,8 +34,8 @@
 #include <iostream>
 #include <algorithm>
 #include <cstring>
+#include <errno.h>
 #include <osg/Math>             // isNaN
-#include <plib/netSocket.h>
 
 #include <simgear/misc/stdint.hxx>
 #include <simgear/timing/timestamp.hxx>
 #include <simgear/props/props.hxx>
 
 #include <AIModel/AIManager.hxx>
+#include <AIModel/AIMultiplayer.hxx>
 #include <Main/fg_props.hxx>
 #include "multiplaymgr.hxx"
 #include "mpmessages.hxx"
+#include <FDM/flightProperties.hxx>
 
 using namespace std;
 
@@ -57,11 +59,18 @@ using namespace std;
 const char sMULTIPLAYMGR_BID[] = "$Id$";
 const char sMULTIPLAYMGR_HID[] = MULTIPLAYTXMGR_HID;
 
+struct IdPropertyList {
+  unsigned id;
+  const char* name;
+  simgear::props::Type type;
+};
+static const IdPropertyList* findProperty(unsigned id);
+  
 // A static map of protocol property id values to property paths,
 // This should be extendable dynamically for every specific aircraft ...
 // For now only that static list
-const FGMultiplayMgr::IdPropertyList
-FGMultiplayMgr::sIdPropertyList[] = {
+static const IdPropertyList sIdPropertyList[] = {
   {100, "surface-positions/left-aileron-pos-norm",  simgear::props::FLOAT},
   {101, "surface-positions/right-aileron-pos-norm", simgear::props::FLOAT},
   {102, "surface-positions/elevator-pos-norm",      simgear::props::FLOAT},
@@ -158,6 +167,13 @@ FGMultiplayMgr::sIdPropertyList[] = {
   {1100, "sim/model/variant", simgear::props::INT},
   {1101, "sim/model/livery/file", simgear::props::STRING},
 
+  {1200, "environment/wildfire/data", simgear::props::STRING},
+  {1201, "environment/contrail", simgear::props::INT},
+
+  {1300, "tanker", simgear::props::INT},
+
+  {1400, "scenery/events", simgear::props::STRING},
+
   {10001, "sim/multiplay/transmission-freq-hz",  simgear::props::STRING},
   {10002, "sim/multiplay/chat",  simgear::props::STRING},
 
@@ -225,34 +241,33 @@ FGMultiplayMgr::sIdPropertyList[] = {
   {10319, "sim/multiplay/generic/int[19]", simgear::props::INT}
 };
 
-const unsigned
-FGMultiplayMgr::numProperties = (sizeof(FGMultiplayMgr::sIdPropertyList)
-                                 / sizeof(FGMultiplayMgr::sIdPropertyList[0]));
+const unsigned int numProperties = (sizeof(sIdPropertyList)
+                                 / sizeof(sIdPropertyList[0]));
 
 // Look up a property ID using binary search.
 namespace
 {
   struct ComparePropertyId
   {
-    bool operator()(const FGMultiplayMgr::IdPropertyList& lhs,
-                    const FGMultiplayMgr::IdPropertyList& rhs)
+    bool operator()(const IdPropertyList& lhs,
+                    const IdPropertyList& rhs)
     {
       return lhs.id < rhs.id;
     }
-    bool operator()(const FGMultiplayMgr::IdPropertyList& lhs,
+    bool operator()(const IdPropertyList& lhs,
                     unsigned id)
     {
       return lhs.id < id;
     }
     bool operator()(unsigned id,
-                    const FGMultiplayMgr::IdPropertyList& rhs)
+                    const IdPropertyList& rhs)
     {
       return id < rhs.id;
     }
-  };
-    
+  };    
 }
-const FGMultiplayMgr::IdPropertyList* FGMultiplayMgr::findProperty(unsigned id)
+
+const IdPropertyList* findProperty(unsigned id)
 {
   std::pair<const IdPropertyList*, const IdPropertyList*> result
     = std::equal_range(sIdPropertyList, sIdPropertyList + numProperties, id,
@@ -272,8 +287,7 @@ namespace
     const xdr_data_t* xdr = data;
     while (xdr < end) {
       unsigned id = XDR_decode_uint32(*xdr);
-      const FGMultiplayMgr::IdPropertyList* plist
-        = FGMultiplayMgr::findProperty(id);
+      const IdPropertyList* plist = findProperty(id);
     
       if (plist) {
         xdr++;
@@ -332,6 +346,24 @@ namespace
     return true;
   }
 }
+
+class MPPropertyListener : public SGPropertyChangeListener
+{
+public:
+  MPPropertyListener(FGMultiplayMgr* mp) :
+    _multiplay(mp)
+  {
+  }
+
+  virtual void childAdded(SGPropertyNode*, SGPropertyNode*)
+  {
+    _multiplay->setPropertiesChanged();
+  }
+
+private:
+  FGMultiplayMgr* _multiplay;
+};
+
 //////////////////////////////////////////////////////////////////////
 //
 //  MultiplayMgr constructor
@@ -339,9 +371,9 @@ namespace
 //////////////////////////////////////////////////////////////////////
 FGMultiplayMgr::FGMultiplayMgr() 
 {
-  mSocket        = 0;
   mInitialised   = false;
   mHaveServer    = false;
+  mListener = NULL;
 } // FGMultiplayMgr::FGMultiplayMgr()
 //////////////////////////////////////////////////////////////////////
 
@@ -352,7 +384,7 @@ FGMultiplayMgr::FGMultiplayMgr()
 //////////////////////////////////////////////////////////////////////
 FGMultiplayMgr::~FGMultiplayMgr() 
 {
-  Close();
+  
 } // FGMultiplayMgr::~FGMultiplayMgr()
 //////////////////////////////////////////////////////////////////////
 
@@ -361,7 +393,7 @@ FGMultiplayMgr::~FGMultiplayMgr()
 //  Initialise object
 //
 //////////////////////////////////////////////////////////////////////
-bool
+void
 FGMultiplayMgr::init (void) 
 {
   //////////////////////////////////////////////////
@@ -369,33 +401,47 @@ FGMultiplayMgr::init (void)
   //////////////////////////////////////////////////
   if (mInitialised) {
     SG_LOG(SG_NETWORK, SG_WARN, "FGMultiplayMgr::init - already initialised");
-    return false;
+    return;
   }
+  
+  fgSetBool("/sim/multiplay/online", false);
+  
   //////////////////////////////////////////////////
   //  Set members from property values
   //////////////////////////////////////////////////
   short rxPort = fgGetInt("/sim/multiplay/rxport");
   string rxAddress = fgGetString("/sim/multiplay/rxhost");
-  short txPort = fgGetInt("/sim/multiplay/txport");
+  short txPort = fgGetInt("/sim/multiplay/txport", 5000);
   string txAddress = fgGetString("/sim/multiplay/txhost");
+  
+  int hz = fgGetInt("/sim/multiplay/tx-rate-hz", 10);
+  if (hz < 1) {
+    hz = 10;
+  }
+  
+  mDt = 1.0 / hz;
+  mTimeUntilSend = 0.0;
+  
   mCallsign = fgGetString("/sim/multiplay/callsign");
-  if (txPort > 0 && !txAddress.empty()) {
+  if ((!txAddress.empty()) && (txAddress!="0")) {
     mServer.set(txAddress.c_str(), txPort);
     if (strncmp (mServer.getHost(), "0.0.0.0", 8) == 0) {
       mHaveServer = false;
-      SG_LOG(SG_NETWORK, SG_DEBUG,
-        "FGMultiplayMgr - could not resolve '"
-        << txAddress << "', Multiplayermode disabled");
+      SG_LOG(SG_NETWORK, SG_ALERT,
+        "FGMultiplayMgr - Could not resolve '"
+        << txAddress << "'. Multiplayer mode disabled.");
+      return;
     } else {
+        SG_LOG(SG_NETWORK, SG_INFO, "FGMultiplayMgr - have server");
       mHaveServer = true;
     }
     if (rxPort <= 0)
       rxPort = txPort;
   }
   if (rxPort <= 0) {
-    SG_LOG(SG_NETWORK, SG_DEBUG,
-      "FGMultiplayMgr - No receiver port, Multiplayermode disabled");
-    return (false);
+    SG_LOG(SG_NETWORK, SG_INFO,
+      "FGMultiplayMgr - No receiver port. Multiplayer mode disabled.");
+    return;
   }
   if (mCallsign.empty())
     mCallsign = "JohnDoe"; // FIXME: use getpwuid
@@ -404,23 +450,29 @@ FGMultiplayMgr::init (void)
   SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-rxaddress="<<rxAddress );
   SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-rxport= "<<rxPort);
   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.reset(new simgear::Socket());
   if (!mSocket->open(false)) {
-    SG_LOG( SG_NETWORK, SG_DEBUG,
-            "FGMultiplayMgr::init - Failed to create data socket" );
-    return false;
+    SG_LOG( SG_NETWORK, SG_WARN,
+            "FGMultiplayMgr - Failed to create data socket. Multiplayer mode disabled." );
+    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;
+    SG_LOG( SG_NETWORK, SG_ALERT,
+            "FGMultiplayMgr - Failed to bind receive socket. Multiplayer mode disabled. "
+            << strerror(errno) << "(errno " << errno << ")");
+    return;
   }
+  
+  mPropertiesChanged = true;
+  mListener = new MPPropertyListener(this);
+  globals->get_props()->addChangeListener(mListener, false);
+  
+  fgSetBool("/sim/multiplay/online", true);
   mInitialised = true;
-  return true;
+
+  SG_LOG(SG_NETWORK, SG_ALERT, "Multiplayer mode active!");
 } // FGMultiplayMgr::init()
 //////////////////////////////////////////////////////////////////////
 
@@ -431,19 +483,39 @@ FGMultiplayMgr::init (void)
 //
 //////////////////////////////////////////////////////////////////////
 void
-FGMultiplayMgr::Close (void) 
+FGMultiplayMgr::shutdown (void) 
 {
-  mMultiPlayerMap.clear();
-
-  if (mSocket) {
+  fgSetBool("/sim/multiplay/online", false);
+  
+  if (mSocket.get()) {
     mSocket->close();
-    delete mSocket;
-    mSocket = 0;
+    mSocket.reset(); 
   }
+  
+  MultiPlayerMap::iterator it = mMultiPlayerMap.begin(),
+    end = mMultiPlayerMap.end();
+  for (; it != end; ++it) {
+    it->second->setDie(true);
+  }
+  mMultiPlayerMap.clear();
+  
+  if (mListener) {
+    globals->get_props()->removeChangeListener(mListener);
+    delete mListener;
+    mListener = NULL;
+  }
+  
   mInitialised = false;
 } // FGMultiplayMgr::Close(void)
 //////////////////////////////////////////////////////////////////////
 
+void
+FGMultiplayMgr::reinit()
+{
+  shutdown();
+  init();
+}
+
 //////////////////////////////////////////////////////////////////////
 //
 //  Description: Sends the position data for the local position.
@@ -462,12 +534,12 @@ union FGMultiplayMgr::MsgBuf
 
     T_MsgHdr* msgHdr()
     {
-        return reinterpret_cast<T_MsgHdr*>(Msg);
+        return &Header;
     }
 
     const T_MsgHdr* msgHdr() const
     {
-        return reinterpret_cast<const T_MsgHdr*>(Msg);
+        return reinterpret_cast<const T_MsgHdr*>(&Header);
     }
 
     T_PositionMsg* posMsg()
@@ -509,149 +581,198 @@ union FGMultiplayMgr::MsgBuf
      */
     xdr_data_t* propsRecvdEnd()
     {
-        return reinterpret_cast<xdr_data_t*>(Msg + msgHdr()->MsgLen);
+        return reinterpret_cast<xdr_data_t*>(Msg + Header.MsgLen);
     }
 
     const xdr_data_t* propsRecvdEnd() const
     {
-        return reinterpret_cast<const xdr_data_t*>(Msg + msgHdr()->MsgLen);
+        return reinterpret_cast<const xdr_data_t*>(Msg + Header.MsgLen);
     }
     
     xdr_data2_t double_val;
     char Msg[MAX_PACKET_SIZE];
+    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)&&
+      fgGetBool("/sim/multiplay/freeze-on-replay",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((*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_uint32(len);
-            if (len != 0)
+            *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()
 
@@ -705,7 +826,7 @@ FGMultiplayMgr::SendTextMessage(const string &MsgText)
 //  
 //////////////////////////////////////////////////////////////////////
 void
-FGMultiplayMgr::Update(void
+FGMultiplayMgr::update(double dt
 {
   if (!mInitialised)
     return;
@@ -713,6 +834,14 @@ FGMultiplayMgr::Update(void)
   /// Just for expiry
   long stamp = SGTimeStamp::now().getSeconds();
 
+  //////////////////////////////////////////////////
+  //  Send if required
+  //////////////////////////////////////////////////
+  mTimeUntilSend -= dt;
+  if (mTimeUntilSend <= 0.0) {
+    Send();
+  }
+
   //////////////////////////////////////////////////
   //  Read the receive socket and process any data
   //////////////////////////////////////////////////
@@ -725,17 +854,34 @@ FGMultiplayMgr::Update(void)
     //  returned will only be that of the next
     //  packet waiting to be processed.
     //////////////////////////////////////////////////
-    netAddress SenderAddress;
-    bytes = mSocket->recvfrom(msgBuf.Msg, sizeof(msgBuf.Msg), 0,
+    simgear::IPAddress SenderAddress;
+    int RecvStatus = mSocket->recvfrom(msgBuf.Msg, sizeof(msgBuf.Msg), 0,
                               &SenderAddress);
     //////////////////////////////////////////////////
     //  no Data received
     //////////////////////////////////////////////////
-    if (bytes <= 0) {
-      if (errno != EAGAIN && errno != 0) // MSVC output "NoError" otherwise
-        perror("FGMultiplayMgr::MP_ProcessData");
-      break;
+    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
+        break;
+    }
+
+    if (RecvStatus<0)
+    {
+        SG_LOG(SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - Unable to receive data. "
+               << strerror(errno) << "(errno " << errno << ")");
+        break;
     }
+
+    // status is positive: bytes received
+    bytes = (ssize_t) RecvStatus;
     if (bytes <= static_cast<ssize_t>(sizeof(T_MsgHdr))) {
       SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - "
               << "received message with insufficient data" );
@@ -758,10 +904,10 @@ FGMultiplayMgr::Update(void)
     }
     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) {
+    if (static_cast<ssize_t>(MsgHdr->MsgLen) != bytes) {
       SG_LOG(SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - "
              << "message from " << MsgHdr->Callsign << " has invalid length!");
       break;
@@ -802,6 +948,140 @@ FGMultiplayMgr::Update(void)
 } // FGMultiplayMgr::ProcessData(void)
 //////////////////////////////////////////////////////////////////////
 
+void
+FGMultiplayMgr::Send()
+{
+  using namespace simgear;
+  
+  findProperties();
+    
+  // smooth the send rate, by adjusting based on the 'remainder' time, which
+  // is how -ve mTimeUntilSend is. Watch for large values and ignore them,
+  // however.
+    if ((mTimeUntilSend < 0.0) && (fabs(mTimeUntilSend) < mDt)) {
+      mTimeUntilSend = mDt + mTimeUntilSend;
+    } else {
+      mTimeUntilSend = mDt;
+    }
+
+    double sim_time = globals->get_sim_time_sec();
+    static double lastTime = 0.0;
+    
+   // SG_LOG(SG_GENERAL, SG_INFO, "actual dt=" << sim_time - lastTime);
+    lastTime = sim_time;
+    
+    FlightProperties ifce;
+
+    // put together a motion info struct, you will get that later
+    // from FGInterface directly ...
+    FGExternalMotionData motionInfo;
+
+    // The current simulation time we need to update for,
+    // note that the simulation time is updated before calling all the
+    // update methods. Thus it contains the time intervals *end* time.
+    // The FDM is already run, so the states belong to that time.
+    motionInfo.time = sim_time;
+    motionInfo.lag = mDt;
+
+    // These are for now converted from lat/lon/alt and euler angles.
+    // But this should change in FGInterface ...
+    double lon = ifce.get_Longitude();
+    double lat = ifce.get_Latitude();
+    // first the aprioriate structure for the geodetic one
+    SGGeod geod = SGGeod::fromRadFt(lon, lat, ifce.get_Altitude());
+    // Convert to cartesion coordinate
+    motionInfo.position = SGVec3d::fromGeod(geod);
+    
+    // The quaternion rotating from the earth centered frame to the
+    // horizontal local frame
+    SGQuatf qEc2Hl = SGQuatf::fromLonLatRad((float)lon, (float)lat);
+    // The orientation wrt the horizontal local frame
+    float heading = ifce.get_Psi();
+    float pitch = ifce.get_Theta();
+    float roll = ifce.get_Phi();
+    SGQuatf hlOr = SGQuatf::fromYawPitchRoll(heading, pitch, roll);
+    // The orientation of the vehicle wrt the earth centered frame
+    motionInfo.orientation = qEc2Hl*hlOr;
+
+    if (!globals->get_subsystem("flight")->is_suspended()) {
+      // velocities
+      motionInfo.linearVel = SG_FEET_TO_METER*SGVec3f(ifce.get_uBody(),
+                                                      ifce.get_vBody(),
+                                                      ifce.get_wBody());
+      motionInfo.angularVel = SGVec3f(ifce.get_P_body(),
+                                      ifce.get_Q_body(),
+                                      ifce.get_R_body());
+      
+      // accels, set that to zero for now.
+      // Angular accelerations are missing from the interface anyway,
+      // linear accelerations are screwed up at least for JSBSim.
+//  motionInfo.linearAccel = SG_FEET_TO_METER*SGVec3f(ifce.get_U_dot_body(),
+//                                                    ifce.get_V_dot_body(),
+//                                                    ifce.get_W_dot_body());
+      motionInfo.linearAccel = SGVec3f::zeros();
+      motionInfo.angularAccel = SGVec3f::zeros();
+    } else {
+      // if the interface is suspendend, prevent the client from
+      // wild extrapolations
+      motionInfo.linearVel = SGVec3f::zeros();
+      motionInfo.angularVel = SGVec3f::zeros();
+      motionInfo.linearAccel = SGVec3f::zeros();
+      motionInfo.angularAccel = SGVec3f::zeros();
+    }
+
+    // now send the properties
+    PropertyMap::iterator it;
+    for (it = mPropertyMap.begin(); it != mPropertyMap.end(); ++it) {
+      FGPropertyData* pData = new FGPropertyData;
+      pData->id = it->first;
+      pData->type = it->second->getType();
+      
+      switch (pData->type) {
+        case props::INT:
+        case props::LONG:
+        case props::BOOL:
+          pData->int_value = it->second->getIntValue();
+          break;
+        case props::FLOAT:
+        case props::DOUBLE:
+          pData->float_value = it->second->getFloatValue();
+          break;
+        case props::STRING:
+        case props::UNSPECIFIED:
+          {
+            // FIXME: We assume unspecified are strings for the moment.
+
+            const char* cstr = it->second->getStringValue();
+            int len = strlen(cstr);
+            
+            if (len > 0)
+            {            
+              pData->string_value = new char[len + 1];
+              strcpy(pData->string_value, cstr);
+            }
+            else
+            {
+              // Size 0 - ignore
+              pData->string_value = 0;            
+            }
+
+            //cout << " Sending property " << pData->id << " " << pData->type << " " <<  pData->string_value << "\n";
+            break;        
+          }
+        default:
+          // FIXME Currently default to a float. 
+          //cout << "Unknown type when iterating through props: " << pData->type << "\n";
+          pData->float_value = it->second->getFloatValue();
+          break;
+      }
+      
+      motionInfo.properties.push_back(pData);
+    }
+
+    SendMyPosition(motionInfo);
+}
+
+
 //////////////////////////////////////////////////////////////////////
 //
 //  handle a position message
@@ -809,7 +1089,7 @@ FGMultiplayMgr::Update(void)
 //////////////////////////////////////////////////////////////////////
 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)) {
@@ -836,6 +1116,15 @@ FGMultiplayMgr::ProcessPosMsg(const FGMultiplayMgr::MsgBuf& Msg,
   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";
 
@@ -862,19 +1151,20 @@ FGMultiplayMgr::ProcessPosMsg(const FGMultiplayMgr::MsgBuf& Msg,
       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) {
@@ -931,16 +1221,16 @@ FGMultiplayMgr::ProcessPosMsg(const FGMultiplayMgr::MsgBuf& Msg,
           SG_LOG(SG_NETWORK, SG_DEBUG, "Unknown Prop type " << pData->id << " " << pData->type);
           xdr++;
           break;
-      }            
+      }
 
       motionInfo.properties.push_back(pData);
     }
     else
     {
       // We failed to find the property. We'll try the next packet immediately.
-      SG_LOG(SG_NETWORK, SG_INFO, "FGMultiplayMgr::ProcessPosMsg - "
+      SG_LOG(SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::ProcessPosMsg - "
              "message from " << MsgHdr->Callsign << " has unknown property id "
-             << pData->id); 
+             << id); 
     }
   }
  noprops:
@@ -954,12 +1244,12 @@ FGMultiplayMgr::ProcessPosMsg(const FGMultiplayMgr::MsgBuf& Msg,
 //////////////////////////////////////////////////////////////////////
 //
 //  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) {
@@ -1019,7 +1309,7 @@ FGMultiplayMgr::addMultiplayer(const std::string& callsign,
   mp->setCallSign(callsign);
   mMultiPlayerMap[callsign] = mp;
 
-  FGAIManager *aiMgr = (FGAIManager*)globals->get_subsystem("ai_model");
+  FGAIManager *aiMgr = (FGAIManager*)globals->get_subsystem("ai-model");
   if (aiMgr) {
     aiMgr->attach(mp);
 
@@ -1039,3 +1329,29 @@ FGMultiplayMgr::getMultiplayer(const std::string& callsign)
   else
     return 0;
 }
+
+void
+FGMultiplayMgr::findProperties()
+{
+  if (!mPropertiesChanged) {
+    return;
+  }
+  
+  mPropertiesChanged = false;
+  
+  for (unsigned i = 0; i < numProperties; ++i) {
+      const char* name = sIdPropertyList[i].name;
+      SGPropertyNode* pNode = globals->get_props()->getNode(name);
+      if (!pNode) {
+        continue;
+      }
+      
+      int id = sIdPropertyList[i].id;
+      if (mPropertyMap.find(id) != mPropertyMap.end()) {
+        continue; // already activated
+      }
+      
+      mPropertyMap[id] = pNode;
+      SG_LOG(SG_NETWORK, SG_DEBUG, "activating MP property:" << pNode->getPath());
+    }
+}