]> git.mxchange.org Git - flightgear.git/blob - src/MultiPlayer/multiplaymgr.cxx
Maik JUSTUS: change /rotors/*/blade1_pos to /rotors/*/blade[0]/position-deg,
[flightgear.git] / src / MultiPlayer / multiplaymgr.cxx
1 //////////////////////////////////////////////////////////////////////
2 //
3 // multiplaymgr.hpp
4 //
5 // Written by Duncan McCreanor, started February 2003.
6 // duncan.mccreanor@airservicesaustralia.com
7 //
8 // Copyright (C) 2003  Airservices Australia
9 // Copyright (C) 2005  Oliver Schroeder
10 // Copyright (C) 2006  Mathias Froehlich
11 //
12 // This program is free software; you can redistribute it and/or
13 // modify it under the terms of the GNU General Public License as
14 // published by the Free Software Foundation; either version 2 of the
15 // License, or (at your option) any later version.
16 //
17 // This program is distributed in the hope that it will be useful, but
18 // WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20 // General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
25 //
26 // $Id$
27 //  
28 //////////////////////////////////////////////////////////////////////
29
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33
34 #include <plib/netSocket.h>
35
36 #include <simgear/misc/stdint.hxx>
37 #include <simgear/timing/timestamp.hxx>
38 #include <simgear/debug/logstream.hxx>
39 #include <simgear/props/props.hxx>
40
41 #include <AIModel/AIManager.hxx>
42 #include <Main/fg_props.hxx>
43 #include "multiplaymgr.hxx"
44 #include "mpmessages.hxx"
45
46 #define MAX_PACKET_SIZE 1200
47 #define MAX_TEXT_SIZE 128
48
49 // These constants are provided so that the ident 
50 // command can list file versions
51 const char sMULTIPLAYMGR_BID[] = "$Id$";
52 const char sMULTIPLAYMGR_HID[] = MULTIPLAYTXMGR_HID;
53
54 // A static map of protocol property id values to property paths,
55 // This should be extendable dynamically for every specific aircraft ...
56 // For now only that static list
57 FGMultiplayMgr::IdPropertyList
58 FGMultiplayMgr::sIdPropertyList[] = {
59   {100, "surface-positions/left-aileron-pos-norm",  SGPropertyNode::FLOAT},
60   {101, "surface-positions/right-aileron-pos-norm", SGPropertyNode::FLOAT},
61   {102, "surface-positions/elevator-pos-norm",      SGPropertyNode::FLOAT},
62   {103, "surface-positions/rudder-pos-norm",        SGPropertyNode::FLOAT},
63   {104, "surface-positions/flap-pos-norm",          SGPropertyNode::FLOAT},
64   {105, "surface-positions/speedbrake-pos-norm",    SGPropertyNode::FLOAT},
65   {106, "gear/tailhook/position-norm",              SGPropertyNode::FLOAT},
66
67   {200, "gear/gear[0]/compression-norm",           SGPropertyNode::FLOAT},
68   {201, "gear/gear[0]/position-norm",              SGPropertyNode::FLOAT},
69   {210, "gear/gear[1]/compression-norm",           SGPropertyNode::FLOAT},
70   {211, "gear/gear[1]/position-norm",              SGPropertyNode::FLOAT},
71   {220, "gear/gear[2]/compression-norm",           SGPropertyNode::FLOAT},
72   {221, "gear/gear[2]/position-norm",              SGPropertyNode::FLOAT},
73   {230, "gear/gear[3]/compression-norm",           SGPropertyNode::FLOAT},
74   {231, "gear/gear[3]/position-norm",              SGPropertyNode::FLOAT},
75   {240, "gear/gear[4]/compression-norm",           SGPropertyNode::FLOAT},
76   {241, "gear/gear[4]/position-norm",              SGPropertyNode::FLOAT},
77
78   {300, "engines/engine[0]/n1",  SGPropertyNode::FLOAT},
79   {301, "engines/engine[0]/n2",  SGPropertyNode::FLOAT},
80   {302, "engines/engine[0]/rpm", SGPropertyNode::FLOAT},
81   {310, "engines/engine[1]/n1",  SGPropertyNode::FLOAT},
82   {311, "engines/engine[1]/n2",  SGPropertyNode::FLOAT},
83   {312, "engines/engine[1]/rpm", SGPropertyNode::FLOAT},
84   {320, "engines/engine[2]/n1",  SGPropertyNode::FLOAT},
85   {321, "engines/engine[2]/n2",  SGPropertyNode::FLOAT},
86   {322, "engines/engine[2]/rpm", SGPropertyNode::FLOAT},
87   {330, "engines/engine[3]/n1",  SGPropertyNode::FLOAT},
88   {331, "engines/engine[3]/n2",  SGPropertyNode::FLOAT},
89   {332, "engines/engine[3]/rpm", SGPropertyNode::FLOAT},
90   {340, "engines/engine[4]/n1",  SGPropertyNode::FLOAT},
91   {341, "engines/engine[4]/n2",  SGPropertyNode::FLOAT},
92   {342, "engines/engine[4]/rpm", SGPropertyNode::FLOAT},
93   {350, "engines/engine[5]/n1",  SGPropertyNode::FLOAT},
94   {351, "engines/engine[5]/n2",  SGPropertyNode::FLOAT},
95   {352, "engines/engine[5]/rpm", SGPropertyNode::FLOAT},
96   {360, "engines/engine[6]/n1",  SGPropertyNode::FLOAT},
97   {361, "engines/engine[6]/n2",  SGPropertyNode::FLOAT},
98   {362, "engines/engine[6]/rpm", SGPropertyNode::FLOAT},
99   {370, "engines/engine[7]/n1",  SGPropertyNode::FLOAT},
100   {371, "engines/engine[7]/n2",  SGPropertyNode::FLOAT},
101   {372, "engines/engine[7]/rpm", SGPropertyNode::FLOAT},
102   {380, "engines/engine[8]/n1",  SGPropertyNode::FLOAT},
103   {381, "engines/engine[8]/n2",  SGPropertyNode::FLOAT},
104   {382, "engines/engine[8]/rpm", SGPropertyNode::FLOAT},
105   {390, "engines/engine[9]/n1",  SGPropertyNode::FLOAT},
106   {391, "engines/engine[9]/n2",  SGPropertyNode::FLOAT},
107   {392, "engines/engine[9]/rpm", SGPropertyNode::FLOAT},
108
109   {800, "rotors/main/rpm", SGPropertyNode::FLOAT},
110   {801, "rotors/tail/rpm", SGPropertyNode::FLOAT},
111   {810, "rotors/main/blade[0]/position-deg",  SGPropertyNode::FLOAT},
112   {811, "rotors/main/blade[1]/position-deg",  SGPropertyNode::FLOAT},
113   {812, "rotors/main/blade[2]/position-deg",  SGPropertyNode::FLOAT},
114   {813, "rotors/main/blade[3]/position-deg",  SGPropertyNode::FLOAT},
115   {820, "rotors/main/blade[0]/flap-deg",  SGPropertyNode::FLOAT},
116   {821, "rotors/main/blade[1]/flap-deg",  SGPropertyNode::FLOAT},
117   {822, "rotors/main/blade[2]/flap-deg",  SGPropertyNode::FLOAT},
118   {823, "rotors/main/blade[3]/flap-deg",  SGPropertyNode::FLOAT},
119   {830, "rotors/tail/blade[0]/position-deg",  SGPropertyNode::FLOAT},
120   {831, "rotors/tail/blade[1]/position-deg",  SGPropertyNode::FLOAT},
121
122   {1001, "controls/flight/slats",  SGPropertyNode::FLOAT},
123   {1002, "controls/flight/speedbrake",  SGPropertyNode::FLOAT},
124   {1003, "controls/flight/spoilers",  SGPropertyNode::FLOAT},
125   {1004, "controls/gear/gear-down",  SGPropertyNode::FLOAT},
126   {1005, "controls/lighting/nav-lights",  SGPropertyNode::FLOAT},
127   
128   {10001, "sim/multiplay/transmission-freq-hz",  SGPropertyNode::STRING},
129   {10002, "sim/multiplay/chat",  SGPropertyNode::STRING},
130
131   /// termination
132   {0, 0, SGPropertyNode::UNSPECIFIED}
133 };
134
135 //////////////////////////////////////////////////////////////////////
136 //
137 //  MultiplayMgr constructor
138 //
139 //////////////////////////////////////////////////////////////////////
140 FGMultiplayMgr::FGMultiplayMgr() 
141 {
142   mInitialised   = false;
143   mHaveServer    = false;
144 } // FGMultiplayMgr::FGMultiplayMgr()
145 //////////////////////////////////////////////////////////////////////
146
147 //////////////////////////////////////////////////////////////////////
148 //
149 //  MultiplayMgr destructor
150 //
151 //////////////////////////////////////////////////////////////////////
152 FGMultiplayMgr::~FGMultiplayMgr() 
153 {
154   Close();
155 } // FGMultiplayMgr::~FGMultiplayMgr()
156 //////////////////////////////////////////////////////////////////////
157
158 //////////////////////////////////////////////////////////////////////
159 //
160 //  Initialise object
161 //
162 //////////////////////////////////////////////////////////////////////
163 bool
164 FGMultiplayMgr::init (void) 
165 {
166   //////////////////////////////////////////////////
167   //  Initialise object if not already done
168   //////////////////////////////////////////////////
169   if (mInitialised) {
170     SG_LOG(SG_NETWORK, SG_WARN, "FGMultiplayMgr::init - already initialised");
171     return false;
172   }
173   //////////////////////////////////////////////////
174   //  Set members from property values
175   //////////////////////////////////////////////////
176   short rxPort = fgGetInt("/sim/multiplay/rxport");
177   if (rxPort <= 0)
178     rxPort = 5000;
179   mCallsign = fgGetString("/sim/multiplay/callsign");
180   if (mCallsign.empty())
181     // FIXME: use getpwuid
182     mCallsign = "JohnDoe"; 
183   string rxAddress = fgGetString("/sim/multiplay/rxhost");
184   if (rxAddress.empty())
185     rxAddress = "127.0.0.1";
186   short txPort = fgGetInt("/sim/multiplay/txport");
187   string txAddress = fgGetString("/sim/multiplay/txhost");
188   if (txPort > 0 && !txAddress.empty()) {
189     mHaveServer = true;
190     mServer.set(txAddress.c_str(), txPort);
191   }
192   SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-txaddress= "<<txAddress);
193   SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-txport= "<<txPort );
194   SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-rxaddress="<<rxAddress );
195   SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-rxport= "<<rxPort);
196   SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-callsign= "<<mCallsign);
197   mSocket = new netSocket();
198   if (!mSocket->open(false)) {
199     SG_LOG( SG_NETWORK, SG_ALERT,
200             "FGMultiplayMgr::init - Failed to create data socket" );
201     return false;
202   }
203   mSocket->setBlocking(false);
204   mSocket->setBroadcast(true);
205   if (mSocket->bind(rxAddress.c_str(), rxPort) != 0) {
206     perror("bind");
207     SG_LOG( SG_NETWORK, SG_ALERT,
208             "FGMultiplayMgr::Open - Failed to bind receive socket" );
209     return false;
210   }
211   mInitialised = true;
212   return true;
213 } // FGMultiplayMgr::init()
214 //////////////////////////////////////////////////////////////////////
215
216 //////////////////////////////////////////////////////////////////////
217 //
218 //  Closes and deletes the local player object. Closes
219 //  and deletes the tx socket. Resets the object state to unitialised.
220 //
221 //////////////////////////////////////////////////////////////////////
222 void
223 FGMultiplayMgr::Close (void) 
224 {
225   mMultiPlayerMap.clear();
226
227   if (mSocket) {
228     mSocket->close();
229     delete mSocket;
230     mSocket = 0;
231   }
232   mInitialised = false;
233 } // FGMultiplayMgr::Close(void)
234 //////////////////////////////////////////////////////////////////////
235
236 //////////////////////////////////////////////////////////////////////
237 //
238 //  Description: Sends the position data for the local position.
239 //
240 //////////////////////////////////////////////////////////////////////
241 void
242 FGMultiplayMgr::SendMyPosition(const FGExternalMotionData& motionInfo)
243 {
244   if ((! mInitialised) || (! mHaveServer)) {
245     if (! mInitialised)
246       SG_LOG( SG_NETWORK, SG_ALERT,
247               "FGMultiplayMgr::SendMyPosition - not initialised" );
248     if (! mHaveServer)
249       SG_LOG( SG_NETWORK, SG_ALERT,
250               "FGMultiplayMgr::SendMyPosition - no server" );
251     return;
252   }
253
254   T_PositionMsg PosMsg;
255   strncpy(PosMsg.Model, fgGetString("/sim/model/path"), MAX_MODEL_NAME_LEN);
256   PosMsg.Model[MAX_MODEL_NAME_LEN - 1] = '\0';
257   
258   PosMsg.time = XDR_encode_double (motionInfo.time);
259   PosMsg.lag = XDR_encode_double (motionInfo.lag);
260   for (unsigned i = 0 ; i < 3; ++i)
261     PosMsg.position[i] = XDR_encode_double (motionInfo.position(i));
262   SGVec3f angleAxis;
263   motionInfo.orientation.getAngleAxis(angleAxis);
264   for (unsigned i = 0 ; i < 3; ++i)
265     PosMsg.orientation[i] = XDR_encode_float (angleAxis(i));
266   for (unsigned i = 0 ; i < 3; ++i)
267     PosMsg.linearVel[i] = XDR_encode_float (motionInfo.linearVel(i));
268   for (unsigned i = 0 ; i < 3; ++i)
269     PosMsg.angularVel[i] = XDR_encode_float (motionInfo.angularVel(i));
270   for (unsigned i = 0 ; i < 3; ++i)
271     PosMsg.linearAccel[i] = XDR_encode_float (motionInfo.linearAccel(i));
272   for (unsigned i = 0 ; i < 3; ++i)
273     PosMsg.angularAccel[i] = XDR_encode_float (motionInfo.angularAccel(i));
274
275   char Msg[MAX_PACKET_SIZE];
276   memcpy(Msg + sizeof(T_MsgHdr), &PosMsg, sizeof(T_PositionMsg));
277   
278   char* ptr = Msg + sizeof(T_MsgHdr) + sizeof(T_PositionMsg);
279   std::vector<FGPropertyData*>::const_iterator it;
280   it = motionInfo.properties.begin();
281   //cout << "OUTPUT PROPERTIES\n";
282   while (it != motionInfo.properties.end()
283          && ptr < (Msg + MAX_PACKET_SIZE - sizeof(xdr_data_t))) {
284              
285     // First elements is the ID
286     xdr_data_t xdr = XDR_encode_uint32((*it)->id);
287     memcpy(ptr, &xdr, sizeof(xdr_data_t));
288     ptr += sizeof(xdr_data_t);
289     
290     // The actual data representation depends on the type
291     switch ((*it)->type) {
292       case SGPropertyNode::INT:        
293       case SGPropertyNode::BOOL:        
294       case SGPropertyNode::LONG:        
295         xdr = XDR_encode_uint32((*it)->int_value);
296         memcpy(ptr, &xdr, sizeof(xdr_data_t));
297         ptr += sizeof(xdr_data_t);
298         //cout << "Prop:" << (*it)->id << " " << (*it)->type << " "<< (*it)->int_value << "\n";
299         break;
300       case SGPropertyNode::FLOAT:
301       case SGPropertyNode::DOUBLE:
302         xdr = XDR_encode_float((*it)->float_value);;
303         memcpy(ptr, &xdr, sizeof(xdr_data_t));
304         ptr += sizeof(xdr_data_t);
305         //cout << "Prop:" << (*it)->id << " " << (*it)->type << " "<< (*it)->float_value << "\n";
306         break;
307       case SGPropertyNode::STRING:
308       case SGPropertyNode::UNSPECIFIED:
309         {
310           // String is complicated. It consists of
311           // The length of the string
312           // The string itself
313           // Padding to the nearest 4-bytes.        
314           const char* lcharptr = (*it)->string_value;
315           
316           if (lcharptr != 0)
317           {
318             // Add the length         
319             ////cout << "String length: " << strlen(lcharptr) << "\n";
320             uint32_t len = strlen(lcharptr);
321             //cout << "String length unint32: " << len << "\n";
322             xdr = XDR_encode_uint32(len);
323             memcpy(ptr, &xdr, sizeof(xdr_data_t));
324             ptr += sizeof(xdr_data_t);
325             
326             if (len != 0)
327             {
328
329               // Now the text itself          
330               int lcount = 0;
331               while ((*lcharptr != '\0') && (lcount < MAX_TEXT_SIZE)) 
332               {
333                 xdr = XDR_encode_int8(*lcharptr);
334                 memcpy(ptr, &xdr, sizeof(xdr_data_t));
335                 ptr += sizeof(xdr_data_t);
336                 lcharptr++;
337                 lcount++;          
338               }
339
340               //cout << "Prop:" << (*it)->id << " " << (*it)->type << " " << len << " " << (*it)->string_value;
341
342               // Now pad if required
343               while ((lcount % 4) != 0)
344               {
345                 xdr = XDR_encode_int8(0);
346                 memcpy(ptr, &xdr, sizeof(xdr_data_t));
347                 ptr += sizeof(xdr_data_t);
348                 lcount++;          
349                 //cout << "0";
350               }
351               
352               //cout << "\n";
353             }
354           }
355           else
356           {
357             // Nothing to encode
358             xdr = XDR_encode_uint32(0);
359             memcpy(ptr, &xdr, sizeof(xdr_data_t));
360             ptr += sizeof(xdr_data_t);
361             //cout << "Prop:" << (*it)->id << " " << (*it)->type << " 0\n";
362           }
363            
364         }
365         break;
366         
367       default:
368         //cout << " Unknown Type: " << (*it)->type << "\n";
369         xdr = XDR_encode_float((*it)->float_value);;
370         memcpy(ptr, &xdr, sizeof(xdr_data_t));
371         ptr += sizeof(xdr_data_t);
372         //cout << "Prop:" << (*it)->id << " " << (*it)->type << " "<< (*it)->float_value << "\n";
373         break;
374     }
375         
376     ++it;
377   }
378
379   T_MsgHdr MsgHdr;
380   FillMsgHdr(&MsgHdr, POS_DATA_ID, ptr - Msg);
381   memcpy(Msg, &MsgHdr, sizeof(T_MsgHdr));
382
383   mSocket->sendto(Msg, ptr - Msg, 0, &mServer);
384   SG_LOG(SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::SendMyPosition");
385 } // FGMultiplayMgr::SendMyPosition()
386 //////////////////////////////////////////////////////////////////////
387
388 //////////////////////////////////////////////////////////////////////
389 //
390 //  Name: SendTextMessage
391 //  Description: Sends a message to the player. The message must
392 //  contain a valid and correctly filled out header and optional
393 //  message body.
394 //
395 //////////////////////////////////////////////////////////////////////
396 void
397 FGMultiplayMgr::SendTextMessage(const string &MsgText)
398 {
399   if (!mInitialised || !mHaveServer)
400     return;
401
402   T_MsgHdr MsgHdr;
403   FillMsgHdr(&MsgHdr, CHAT_MSG_ID);
404   //////////////////////////////////////////////////
405   // Divide the text string into blocks that fit
406   // in the message and send the blocks.
407   //////////////////////////////////////////////////
408   unsigned iNextBlockPosition = 0;
409   T_ChatMsg ChatMsg;
410   
411   char Msg[sizeof(T_MsgHdr) + sizeof(T_ChatMsg)];
412   while (iNextBlockPosition < MsgText.length()) {
413     strncpy (ChatMsg.Text, 
414              MsgText.substr(iNextBlockPosition, MAX_CHAT_MSG_LEN - 1).c_str(),
415              MAX_CHAT_MSG_LEN);
416     ChatMsg.Text[MAX_CHAT_MSG_LEN - 1] = '\0';
417     memcpy (Msg, &MsgHdr, sizeof(T_MsgHdr));
418     memcpy (Msg + sizeof(T_MsgHdr), &ChatMsg, sizeof(T_ChatMsg));
419     mSocket->sendto (Msg, sizeof(T_MsgHdr) + sizeof(T_ChatMsg), 0, &mServer);
420     iNextBlockPosition += MAX_CHAT_MSG_LEN - 1;
421
422   }
423   
424   
425 } // FGMultiplayMgr::SendTextMessage ()
426 //////////////////////////////////////////////////////////////////////
427
428 //////////////////////////////////////////////////////////////////////
429 //
430 //  Name: ProcessData
431 //  Description: Processes data waiting at the receive socket. The
432 //  processing ends when there is no more data at the socket.
433 //  
434 //////////////////////////////////////////////////////////////////////
435 void
436 FGMultiplayMgr::Update(void) 
437 {
438   if (!mInitialised)
439     return;
440
441   /// Just for expiry
442   SGTimeStamp timestamper;
443   timestamper.stamp();
444   long stamp = timestamper.get_seconds();
445
446   //////////////////////////////////////////////////
447   //  Read the receive socket and process any data
448   //////////////////////////////////////////////////
449   int bytes;
450   do {
451     char Msg[MAX_PACKET_SIZE];
452     //////////////////////////////////////////////////
453     //  Although the recv call asks for 
454     //  MAX_PACKET_SIZE of data, the number of bytes
455     //  returned will only be that of the next
456     //  packet waiting to be processed.
457     //////////////////////////////////////////////////
458     netAddress SenderAddress;
459     bytes = mSocket->recvfrom(Msg, sizeof(Msg), 0, &SenderAddress);
460     //////////////////////////////////////////////////
461     //  no Data received
462     //////////////////////////////////////////////////
463     if (bytes <= 0) {
464       if (errno != EAGAIN && errno != 0) // MSVC output "NoError" otherwise
465         perror("FGMultiplayMgr::MP_ProcessData");
466       break;
467     }
468     if (bytes <= sizeof(T_MsgHdr)) {
469       SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
470               << "received message with insufficient data" );
471       break;
472     }
473     //////////////////////////////////////////////////
474     //  Read header
475     //////////////////////////////////////////////////
476     T_MsgHdr* MsgHdr = (T_MsgHdr *)Msg;
477     MsgHdr->Magic       = XDR_decode_uint32 (MsgHdr->Magic);
478     MsgHdr->Version     = XDR_decode_uint32 (MsgHdr->Version);
479     MsgHdr->MsgId       = XDR_decode_uint32 (MsgHdr->MsgId);
480     MsgHdr->MsgLen      = XDR_decode_uint32 (MsgHdr->MsgLen);
481     MsgHdr->ReplyPort   = XDR_decode_uint32 (MsgHdr->ReplyPort);
482     if (MsgHdr->Magic != MSG_MAGIC) {
483       SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
484               << "message has invalid magic number!" );
485     }
486     if (MsgHdr->Version != PROTO_VER) {
487       SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
488               << "message has invalid protocoll number!" );
489     }
490     if (MsgHdr->MsgLen != bytes) {
491       SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
492               << "message has invalid length!" );
493     }
494     //////////////////////////////////////////////////
495     //  Process messages
496     //////////////////////////////////////////////////
497     switch (MsgHdr->MsgId) {
498     case CHAT_MSG_ID:
499       ProcessChatMsg(Msg, SenderAddress);
500       break;
501     case POS_DATA_ID:
502       ProcessPosMsg(Msg, SenderAddress, bytes, stamp);
503       break;
504     case UNUSABLE_POS_DATA_ID:
505     case OLD_OLD_POS_DATA_ID:
506     case OLD_PROP_MSG_ID:
507     case OLD_POS_DATA_ID:
508       break;
509     default:
510       SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
511               << "Unknown message Id received: " << MsgHdr->MsgId );
512       break;
513     }
514   } while (bytes > 0);
515
516   // check for expiry
517   MultiPlayerMap::iterator it = mMultiPlayerMap.begin();
518   while (it != mMultiPlayerMap.end()) {
519     if (it->second->getLastTimestamp() + 10 < stamp) {
520       std::string name = it->first;
521       it->second->setDie(true);
522       mMultiPlayerMap.erase(it);
523       it = mMultiPlayerMap.upper_bound(name);
524     } else
525       ++it;
526   }
527 } // FGMultiplayMgr::ProcessData(void)
528 //////////////////////////////////////////////////////////////////////
529
530 //////////////////////////////////////////////////////////////////////
531 //
532 //  handle a position message
533 //
534 //////////////////////////////////////////////////////////////////////
535 void
536 FGMultiplayMgr::ProcessPosMsg(const char *Msg, netAddress & SenderAddress,
537                               unsigned len, long stamp)
538 {
539   T_MsgHdr* MsgHdr = (T_MsgHdr *)Msg;
540   if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + sizeof(T_PositionMsg)) {
541     SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
542             << "Position message received with insufficient data" );
543     return;
544   }
545   T_PositionMsg* PosMsg = (T_PositionMsg *)(Msg + sizeof(T_MsgHdr));
546   FGExternalMotionData motionInfo;
547   motionInfo.time = XDR_decode_double(PosMsg->time);
548   motionInfo.lag = XDR_decode_double(PosMsg->lag);
549   for (unsigned i = 0; i < 3; ++i)
550     motionInfo.position(i) = XDR_decode_double(PosMsg->position[i]);
551   SGVec3f angleAxis;
552   for (unsigned i = 0; i < 3; ++i)
553     angleAxis(i) = XDR_decode_float(PosMsg->orientation[i]);
554   motionInfo.orientation = SGQuatf::fromAngleAxis(angleAxis);
555   for (unsigned i = 0; i < 3; ++i)
556     motionInfo.linearVel(i) = XDR_decode_float(PosMsg->linearVel[i]);
557   for (unsigned i = 0; i < 3; ++i)
558     motionInfo.angularVel(i) = XDR_decode_float(PosMsg->angularVel[i]);
559   for (unsigned i = 0; i < 3; ++i)
560     motionInfo.linearAccel(i) = XDR_decode_float(PosMsg->linearAccel[i]);
561   for (unsigned i = 0; i < 3; ++i)
562     motionInfo.angularAccel(i) = XDR_decode_float(PosMsg->angularAccel[i]);
563
564
565   //cout << "INPUT MESSAGE\n";
566   xdr_data_t* xdr = (xdr_data_t*) 
567                    (Msg + sizeof(T_MsgHdr) + sizeof(T_PositionMsg));
568   while ((char*)xdr < Msg + len) {
569     FGPropertyData* pData = new FGPropertyData;
570     SGPropertyNode::Type type = SGPropertyNode::UNSPECIFIED;
571     
572     // First element is always the ID
573     pData->id = XDR_decode_uint32(*xdr);
574     //cout << pData->id << " ";
575     xdr++;
576     
577     // Check the ID actually exists and get the type
578     unsigned i = 0;
579     bool found = false;
580     while (FGMultiplayMgr::sIdPropertyList[i].name) 
581     {
582       if (sIdPropertyList[i].id == pData->id)
583       {
584         found = true;
585         pData->type = sIdPropertyList[i].type;
586       } 
587       
588       i++;
589     }
590     
591     if (found == true)
592     {
593       // How we decode the remainder of the property depends on the type
594       switch (pData->type) {
595         case SGPropertyNode::INT:        
596         case SGPropertyNode::BOOL:
597         case SGPropertyNode::LONG:        
598           pData->int_value = XDR_decode_uint32(*xdr);
599           xdr++;
600           //cout << pData->int_value << "\n";
601           break;
602         case SGPropertyNode::FLOAT:
603         case SGPropertyNode::DOUBLE:
604           pData->float_value = XDR_decode_float(*xdr);
605           xdr++;
606           //cout << pData->float_value << "\n";
607           break;
608         case SGPropertyNode::STRING:
609         case SGPropertyNode::UNSPECIFIED:
610           {
611             // String is complicated. It consists of
612             // The length of the string
613             // The string itself
614             // Padding to the nearest 4-bytes.    
615             uint32_t length = XDR_decode_uint32(*xdr);
616             xdr++;
617             //cout << length << " ";
618
619             if ((length > 0) && (length < MAX_TEXT_SIZE))
620             {
621               pData->string_value = new char[length + 1];
622               //cout << " String: ";
623
624               for (int i = 0; i < length; i++)
625               {
626                 pData->string_value[i] = (char) XDR_decode_int8(*xdr);
627                 xdr++;
628                 //cout << pData->string_value[i];
629               }
630
631               pData->string_value[length] = '\0';
632
633               // Now handle the padding
634               while ((length % 4) != 0)
635               {
636                 xdr++;
637                 length++;
638                 //cout << "0";
639               }
640             }
641             else
642             {
643               pData->string_value = new char[1];
644               pData->string_value[0] = '\0';
645             }
646
647             //cout << "\n";
648           }
649           break;
650
651         default:
652           pData->float_value = XDR_decode_float(*xdr);
653           cerr << "Unknown Prop type " << pData->id << " " << pData->type << "\n";
654           xdr++;
655           break;
656       }            
657
658       motionInfo.properties.push_back(pData);
659     }
660     else
661     {
662       // We failed to find the property. We'll try the next packet immediately.
663       //cout << " Unknown\n";
664     }
665   }
666   
667   FGAIMultiplayer* mp = getMultiplayer(MsgHdr->Callsign);
668   if (!mp)
669     mp = addMultiplayer(MsgHdr->Callsign, PosMsg->Model);
670   mp->addMotionInfo(motionInfo, stamp);
671 } // FGMultiplayMgr::ProcessPosMsg()
672 //////////////////////////////////////////////////////////////////////
673
674 //////////////////////////////////////////////////////////////////////
675 //
676 //  handle a chat message
677 //  FIXME: display chat message withi flightgear
678 //
679 //////////////////////////////////////////////////////////////////////
680 void
681 FGMultiplayMgr::ProcessChatMsg(const char *Msg, netAddress& SenderAddress)
682 {
683   T_MsgHdr* MsgHdr = (T_MsgHdr *)Msg;
684   if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + 1) {
685     SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
686             << "Chat message received with insufficient data" );
687     return;
688   }
689   
690   char *MsgBuf = new char[MsgHdr->MsgLen - sizeof(T_MsgHdr)];
691   strncpy(MsgBuf, ((T_ChatMsg *)(Msg + sizeof(T_MsgHdr)))->Text,
692           MsgHdr->MsgLen - sizeof(T_MsgHdr));
693   MsgBuf[MsgHdr->MsgLen - sizeof(T_MsgHdr) - 1] = '\0';
694   
695   T_ChatMsg* ChatMsg = (T_ChatMsg *)(Msg + sizeof(T_MsgHdr));
696   SG_LOG ( SG_NETWORK, SG_ALERT, "Chat [" << MsgHdr->Callsign << "]"
697            << " " << MsgBuf << endl);
698
699   delete [] MsgBuf;
700 } // FGMultiplayMgr::ProcessChatMsg ()
701 //////////////////////////////////////////////////////////////////////
702
703 void
704 FGMultiplayMgr::FillMsgHdr(T_MsgHdr *MsgHdr, int MsgId, unsigned _len)
705 {
706   uint32_t len;
707   switch (MsgId) {
708   case CHAT_MSG_ID:
709     len = sizeof(T_MsgHdr) + sizeof(T_ChatMsg);
710     break;
711   case POS_DATA_ID:
712     len = _len;
713     break;
714   default:
715     len = sizeof(T_MsgHdr);
716     break;
717   }
718   MsgHdr->Magic           = XDR_encode_uint32(MSG_MAGIC);
719   MsgHdr->Version         = XDR_encode_uint32(PROTO_VER);
720   MsgHdr->MsgId           = XDR_encode_uint32(MsgId);
721   MsgHdr->MsgLen          = XDR_encode_uint32(len);
722   MsgHdr->ReplyAddress    = 0; // Are obsolete, keep them for the server for
723   MsgHdr->ReplyPort       = 0; // now
724   strncpy(MsgHdr->Callsign, mCallsign.c_str(), MAX_CALLSIGN_LEN);
725   MsgHdr->Callsign[MAX_CALLSIGN_LEN - 1] = '\0';
726 }
727
728 FGAIMultiplayer*
729 FGMultiplayMgr::addMultiplayer(const std::string& callsign,
730                                const std::string& modelName)
731 {
732   if (0 < mMultiPlayerMap.count(callsign))
733     return mMultiPlayerMap[callsign];
734
735   FGAIMultiplayer* mp = new FGAIMultiplayer;
736   mp->setPath(modelName.c_str());
737   mp->setCallSign(callsign);
738   mMultiPlayerMap[callsign] = mp;
739
740   FGAIManager *aiMgr = (FGAIManager*)globals->get_subsystem("ai_model");
741   if (aiMgr) {
742     aiMgr->attach(mp);
743
744     /// FIXME: that must follow the attach ATM ...
745     unsigned i = 0;
746     while (sIdPropertyList[i].name) {
747       mp->addPropertyId(sIdPropertyList[i].id, sIdPropertyList[i].name);
748       ++i;
749     }
750   }
751
752   return mp;
753 }
754
755 FGAIMultiplayer*
756 FGMultiplayMgr::getMultiplayer(const std::string& callsign)
757 {
758   if (0 < mMultiPlayerMap.count(callsign))
759     return mMultiPlayerMap[callsign];
760   else
761     return 0;
762 }