]> git.mxchange.org Git - flightgear.git/blob - src/MultiPlayer/multiplaymgr.cxx
Maik JUSTUS: aerotowing properties
[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   {900, "sim/hitches/aerotow/tow/length",                       SGPropertyNode::FLOAT},
123   {901, "sim/hitches/aerotow/tow/elastic-constant",             SGPropertyNode::FLOAT},
124   {902, "sim/hitches/aerotow/tow/weight-per-m-kg-m",            SGPropertyNode::FLOAT},
125   {903, "sim/hitches/aerotow/tow/dist",                         SGPropertyNode::FLOAT},
126   {904, "sim/hitches/aerotow/tow/connected-to-property-node",   SGPropertyNode::BOOL},
127   {905, "sim/hitches/aerotow/tow/connectedToAIorMP-callsign",   SGPropertyNode::STRING},
128   {906, "sim/hitches/aerotow/tow/brake-force",                  SGPropertyNode::FLOAT},
129   {907, "sim/hitches/aerotow/tow/end-force-x",                  SGPropertyNode::FLOAT},
130   {908, "sim/hitches/aerotow/tow/end-force-y",                  SGPropertyNode::FLOAT},
131   {909, "sim/hitches/aerotow/tow/end-force-z",                  SGPropertyNode::FLOAT},
132   {930, "sim/hitches/aerotow/is-slave",                         SGPropertyNode::BOOL},
133   {931, "sim/hitches/aerotow/speed-in-tow-direction",           SGPropertyNode::FLOAT},
134   {932, "sim/hitches/aerotow/open",                             SGPropertyNode::BOOL},
135   {933, "sim/hitches/aerotow/local-pos-x",                      SGPropertyNode::FLOAT},
136   {934, "sim/hitches/aerotow/local-pos-y",                      SGPropertyNode::FLOAT},
137   {935, "sim/hitches/aerotow/local-pos-z",                      SGPropertyNode::FLOAT},
138
139   {1001, "controls/flight/slats",  SGPropertyNode::FLOAT},
140   {1002, "controls/flight/speedbrake",  SGPropertyNode::FLOAT},
141   {1003, "controls/flight/spoilers",  SGPropertyNode::FLOAT},
142   {1004, "controls/gear/gear-down",  SGPropertyNode::FLOAT},
143   {1005, "controls/lighting/nav-lights",  SGPropertyNode::FLOAT},
144   
145   {10001, "sim/multiplay/transmission-freq-hz",  SGPropertyNode::STRING},
146   {10002, "sim/multiplay/chat",  SGPropertyNode::STRING},
147
148   /// termination
149   {0, 0, SGPropertyNode::UNSPECIFIED}
150 };
151
152 //////////////////////////////////////////////////////////////////////
153 //
154 //  MultiplayMgr constructor
155 //
156 //////////////////////////////////////////////////////////////////////
157 FGMultiplayMgr::FGMultiplayMgr() 
158 {
159   mInitialised   = false;
160   mHaveServer    = false;
161 } // FGMultiplayMgr::FGMultiplayMgr()
162 //////////////////////////////////////////////////////////////////////
163
164 //////////////////////////////////////////////////////////////////////
165 //
166 //  MultiplayMgr destructor
167 //
168 //////////////////////////////////////////////////////////////////////
169 FGMultiplayMgr::~FGMultiplayMgr() 
170 {
171   Close();
172 } // FGMultiplayMgr::~FGMultiplayMgr()
173 //////////////////////////////////////////////////////////////////////
174
175 //////////////////////////////////////////////////////////////////////
176 //
177 //  Initialise object
178 //
179 //////////////////////////////////////////////////////////////////////
180 bool
181 FGMultiplayMgr::init (void) 
182 {
183   //////////////////////////////////////////////////
184   //  Initialise object if not already done
185   //////////////////////////////////////////////////
186   if (mInitialised) {
187     SG_LOG(SG_NETWORK, SG_WARN, "FGMultiplayMgr::init - already initialised");
188     return false;
189   }
190   //////////////////////////////////////////////////
191   //  Set members from property values
192   //////////////////////////////////////////////////
193   short rxPort = fgGetInt("/sim/multiplay/rxport");
194   if (rxPort <= 0)
195     rxPort = 5000;
196   mCallsign = fgGetString("/sim/multiplay/callsign");
197   if (mCallsign.empty())
198     // FIXME: use getpwuid
199     mCallsign = "JohnDoe"; 
200   string rxAddress = fgGetString("/sim/multiplay/rxhost");
201   if (rxAddress.empty())
202     rxAddress = "127.0.0.1";
203   short txPort = fgGetInt("/sim/multiplay/txport");
204   string txAddress = fgGetString("/sim/multiplay/txhost");
205   if (txPort > 0 && !txAddress.empty()) {
206     mHaveServer = true;
207     mServer.set(txAddress.c_str(), txPort);
208   }
209   SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-txaddress= "<<txAddress);
210   SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-txport= "<<txPort );
211   SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-rxaddress="<<rxAddress );
212   SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-rxport= "<<rxPort);
213   SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-callsign= "<<mCallsign);
214   mSocket = new netSocket();
215   if (!mSocket->open(false)) {
216     SG_LOG( SG_NETWORK, SG_ALERT,
217             "FGMultiplayMgr::init - Failed to create data socket" );
218     return false;
219   }
220   mSocket->setBlocking(false);
221   if (mSocket->bind(rxAddress.c_str(), rxPort) != 0) {
222     perror("bind");
223     SG_LOG( SG_NETWORK, SG_ALERT,
224             "FGMultiplayMgr::Open - Failed to bind receive socket" );
225     return false;
226   }
227   mInitialised = true;
228   return true;
229 } // FGMultiplayMgr::init()
230 //////////////////////////////////////////////////////////////////////
231
232 //////////////////////////////////////////////////////////////////////
233 //
234 //  Closes and deletes the local player object. Closes
235 //  and deletes the tx socket. Resets the object state to unitialised.
236 //
237 //////////////////////////////////////////////////////////////////////
238 void
239 FGMultiplayMgr::Close (void) 
240 {
241   mMultiPlayerMap.clear();
242
243   if (mSocket) {
244     mSocket->close();
245     delete mSocket;
246     mSocket = 0;
247   }
248   mInitialised = false;
249 } // FGMultiplayMgr::Close(void)
250 //////////////////////////////////////////////////////////////////////
251
252 //////////////////////////////////////////////////////////////////////
253 //
254 //  Description: Sends the position data for the local position.
255 //
256 //////////////////////////////////////////////////////////////////////
257 void
258 FGMultiplayMgr::SendMyPosition(const FGExternalMotionData& motionInfo)
259 {
260   if ((! mInitialised) || (! mHaveServer)) {
261     if (! mInitialised)
262       SG_LOG( SG_NETWORK, SG_ALERT,
263               "FGMultiplayMgr::SendMyPosition - not initialised" );
264     if (! mHaveServer)
265       SG_LOG( SG_NETWORK, SG_ALERT,
266               "FGMultiplayMgr::SendMyPosition - no server" );
267     return;
268   }
269
270   T_PositionMsg PosMsg;
271   strncpy(PosMsg.Model, fgGetString("/sim/model/path"), MAX_MODEL_NAME_LEN);
272   PosMsg.Model[MAX_MODEL_NAME_LEN - 1] = '\0';
273   
274   PosMsg.time = XDR_encode_double (motionInfo.time);
275   PosMsg.lag = XDR_encode_double (motionInfo.lag);
276   for (unsigned i = 0 ; i < 3; ++i)
277     PosMsg.position[i] = XDR_encode_double (motionInfo.position(i));
278   SGVec3f angleAxis;
279   motionInfo.orientation.getAngleAxis(angleAxis);
280   for (unsigned i = 0 ; i < 3; ++i)
281     PosMsg.orientation[i] = XDR_encode_float (angleAxis(i));
282   for (unsigned i = 0 ; i < 3; ++i)
283     PosMsg.linearVel[i] = XDR_encode_float (motionInfo.linearVel(i));
284   for (unsigned i = 0 ; i < 3; ++i)
285     PosMsg.angularVel[i] = XDR_encode_float (motionInfo.angularVel(i));
286   for (unsigned i = 0 ; i < 3; ++i)
287     PosMsg.linearAccel[i] = XDR_encode_float (motionInfo.linearAccel(i));
288   for (unsigned i = 0 ; i < 3; ++i)
289     PosMsg.angularAccel[i] = XDR_encode_float (motionInfo.angularAccel(i));
290
291   char Msg[MAX_PACKET_SIZE];
292   memcpy(Msg + sizeof(T_MsgHdr), &PosMsg, sizeof(T_PositionMsg));
293   
294   char* ptr = Msg + sizeof(T_MsgHdr) + sizeof(T_PositionMsg);
295   std::vector<FGPropertyData*>::const_iterator it;
296   it = motionInfo.properties.begin();
297   //cout << "OUTPUT PROPERTIES\n";
298   while (it != motionInfo.properties.end()
299          && ptr < (Msg + MAX_PACKET_SIZE - sizeof(xdr_data_t))) {
300              
301     // First elements is the ID
302     xdr_data_t xdr = XDR_encode_uint32((*it)->id);
303     memcpy(ptr, &xdr, sizeof(xdr_data_t));
304     ptr += sizeof(xdr_data_t);
305     
306     // The actual data representation depends on the type
307     switch ((*it)->type) {
308       case SGPropertyNode::INT:        
309       case SGPropertyNode::BOOL:        
310       case SGPropertyNode::LONG:        
311         xdr = XDR_encode_uint32((*it)->int_value);
312         memcpy(ptr, &xdr, sizeof(xdr_data_t));
313         ptr += sizeof(xdr_data_t);
314         //cout << "Prop:" << (*it)->id << " " << (*it)->type << " "<< (*it)->int_value << "\n";
315         break;
316       case SGPropertyNode::FLOAT:
317       case SGPropertyNode::DOUBLE:
318         xdr = XDR_encode_float((*it)->float_value);;
319         memcpy(ptr, &xdr, sizeof(xdr_data_t));
320         ptr += sizeof(xdr_data_t);
321         //cout << "Prop:" << (*it)->id << " " << (*it)->type << " "<< (*it)->float_value << "\n";
322         break;
323       case SGPropertyNode::STRING:
324       case SGPropertyNode::UNSPECIFIED:
325         {
326           // String is complicated. It consists of
327           // The length of the string
328           // The string itself
329           // Padding to the nearest 4-bytes.        
330           const char* lcharptr = (*it)->string_value;
331           
332           if (lcharptr != 0)
333           {
334             // Add the length         
335             ////cout << "String length: " << strlen(lcharptr) << "\n";
336             uint32_t len = strlen(lcharptr);
337             //cout << "String length unint32: " << len << "\n";
338             xdr = XDR_encode_uint32(len);
339             memcpy(ptr, &xdr, sizeof(xdr_data_t));
340             ptr += sizeof(xdr_data_t);
341             
342             if (len != 0)
343             {
344
345               // Now the text itself          
346               int lcount = 0;
347               while ((*lcharptr != '\0') && (lcount < MAX_TEXT_SIZE)) 
348               {
349                 xdr = XDR_encode_int8(*lcharptr);
350                 memcpy(ptr, &xdr, sizeof(xdr_data_t));
351                 ptr += sizeof(xdr_data_t);
352                 lcharptr++;
353                 lcount++;          
354               }
355
356               //cout << "Prop:" << (*it)->id << " " << (*it)->type << " " << len << " " << (*it)->string_value;
357
358               // Now pad if required
359               while ((lcount % 4) != 0)
360               {
361                 xdr = XDR_encode_int8(0);
362                 memcpy(ptr, &xdr, sizeof(xdr_data_t));
363                 ptr += sizeof(xdr_data_t);
364                 lcount++;          
365                 //cout << "0";
366               }
367               
368               //cout << "\n";
369             }
370           }
371           else
372           {
373             // Nothing to encode
374             xdr = XDR_encode_uint32(0);
375             memcpy(ptr, &xdr, sizeof(xdr_data_t));
376             ptr += sizeof(xdr_data_t);
377             //cout << "Prop:" << (*it)->id << " " << (*it)->type << " 0\n";
378           }
379            
380         }
381         break;
382         
383       default:
384         //cout << " Unknown Type: " << (*it)->type << "\n";
385         xdr = XDR_encode_float((*it)->float_value);;
386         memcpy(ptr, &xdr, sizeof(xdr_data_t));
387         ptr += sizeof(xdr_data_t);
388         //cout << "Prop:" << (*it)->id << " " << (*it)->type << " "<< (*it)->float_value << "\n";
389         break;
390     }
391         
392     ++it;
393   }
394
395   T_MsgHdr MsgHdr;
396   FillMsgHdr(&MsgHdr, POS_DATA_ID, ptr - Msg);
397   memcpy(Msg, &MsgHdr, sizeof(T_MsgHdr));
398
399   mSocket->sendto(Msg, ptr - Msg, 0, &mServer);
400   SG_LOG(SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::SendMyPosition");
401 } // FGMultiplayMgr::SendMyPosition()
402 //////////////////////////////////////////////////////////////////////
403
404 //////////////////////////////////////////////////////////////////////
405 //
406 //  Name: SendTextMessage
407 //  Description: Sends a message to the player. The message must
408 //  contain a valid and correctly filled out header and optional
409 //  message body.
410 //
411 //////////////////////////////////////////////////////////////////////
412 void
413 FGMultiplayMgr::SendTextMessage(const string &MsgText)
414 {
415   if (!mInitialised || !mHaveServer)
416     return;
417
418   T_MsgHdr MsgHdr;
419   FillMsgHdr(&MsgHdr, CHAT_MSG_ID);
420   //////////////////////////////////////////////////
421   // Divide the text string into blocks that fit
422   // in the message and send the blocks.
423   //////////////////////////////////////////////////
424   unsigned iNextBlockPosition = 0;
425   T_ChatMsg ChatMsg;
426   
427   char Msg[sizeof(T_MsgHdr) + sizeof(T_ChatMsg)];
428   while (iNextBlockPosition < MsgText.length()) {
429     strncpy (ChatMsg.Text, 
430              MsgText.substr(iNextBlockPosition, MAX_CHAT_MSG_LEN - 1).c_str(),
431              MAX_CHAT_MSG_LEN);
432     ChatMsg.Text[MAX_CHAT_MSG_LEN - 1] = '\0';
433     memcpy (Msg, &MsgHdr, sizeof(T_MsgHdr));
434     memcpy (Msg + sizeof(T_MsgHdr), &ChatMsg, sizeof(T_ChatMsg));
435     mSocket->sendto (Msg, sizeof(T_MsgHdr) + sizeof(T_ChatMsg), 0, &mServer);
436     iNextBlockPosition += MAX_CHAT_MSG_LEN - 1;
437
438   }
439   
440   
441 } // FGMultiplayMgr::SendTextMessage ()
442 //////////////////////////////////////////////////////////////////////
443
444 //////////////////////////////////////////////////////////////////////
445 //
446 //  Name: ProcessData
447 //  Description: Processes data waiting at the receive socket. The
448 //  processing ends when there is no more data at the socket.
449 //  
450 //////////////////////////////////////////////////////////////////////
451 void
452 FGMultiplayMgr::Update(void) 
453 {
454   if (!mInitialised)
455     return;
456
457   /// Just for expiry
458   SGTimeStamp timestamper;
459   timestamper.stamp();
460   long stamp = timestamper.get_seconds();
461
462   //////////////////////////////////////////////////
463   //  Read the receive socket and process any data
464   //////////////////////////////////////////////////
465   int bytes;
466   do {
467     char Msg[MAX_PACKET_SIZE];
468     //////////////////////////////////////////////////
469     //  Although the recv call asks for 
470     //  MAX_PACKET_SIZE of data, the number of bytes
471     //  returned will only be that of the next
472     //  packet waiting to be processed.
473     //////////////////////////////////////////////////
474     netAddress SenderAddress;
475     bytes = mSocket->recvfrom(Msg, sizeof(Msg), 0, &SenderAddress);
476     //////////////////////////////////////////////////
477     //  no Data received
478     //////////////////////////////////////////////////
479     if (bytes <= 0) {
480       if (errno != EAGAIN && errno != 0) // MSVC output "NoError" otherwise
481         perror("FGMultiplayMgr::MP_ProcessData");
482       break;
483     }
484     if (bytes <= sizeof(T_MsgHdr)) {
485       SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
486               << "received message with insufficient data" );
487       break;
488     }
489     //////////////////////////////////////////////////
490     //  Read header
491     //////////////////////////////////////////////////
492     T_MsgHdr* MsgHdr = (T_MsgHdr *)Msg;
493     MsgHdr->Magic       = XDR_decode_uint32 (MsgHdr->Magic);
494     MsgHdr->Version     = XDR_decode_uint32 (MsgHdr->Version);
495     MsgHdr->MsgId       = XDR_decode_uint32 (MsgHdr->MsgId);
496     MsgHdr->MsgLen      = XDR_decode_uint32 (MsgHdr->MsgLen);
497     MsgHdr->ReplyPort   = XDR_decode_uint32 (MsgHdr->ReplyPort);
498     if (MsgHdr->Magic != MSG_MAGIC) {
499       SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
500               << "message has invalid magic number!" );
501     }
502     if (MsgHdr->Version != PROTO_VER) {
503       SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
504               << "message has invalid protocoll number!" );
505     }
506     if (MsgHdr->MsgLen != bytes) {
507       SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
508               << "message has invalid length!" );
509     }
510     //////////////////////////////////////////////////
511     //  Process messages
512     //////////////////////////////////////////////////
513     switch (MsgHdr->MsgId) {
514     case CHAT_MSG_ID:
515       ProcessChatMsg(Msg, SenderAddress);
516       break;
517     case POS_DATA_ID:
518       ProcessPosMsg(Msg, SenderAddress, bytes, stamp);
519       break;
520     case UNUSABLE_POS_DATA_ID:
521     case OLD_OLD_POS_DATA_ID:
522     case OLD_PROP_MSG_ID:
523     case OLD_POS_DATA_ID:
524       break;
525     default:
526       SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
527               << "Unknown message Id received: " << MsgHdr->MsgId );
528       break;
529     }
530   } while (bytes > 0);
531
532   // check for expiry
533   MultiPlayerMap::iterator it = mMultiPlayerMap.begin();
534   while (it != mMultiPlayerMap.end()) {
535     if (it->second->getLastTimestamp() + 10 < stamp) {
536       std::string name = it->first;
537       it->second->setDie(true);
538       mMultiPlayerMap.erase(it);
539       it = mMultiPlayerMap.upper_bound(name);
540     } else
541       ++it;
542   }
543 } // FGMultiplayMgr::ProcessData(void)
544 //////////////////////////////////////////////////////////////////////
545
546 //////////////////////////////////////////////////////////////////////
547 //
548 //  handle a position message
549 //
550 //////////////////////////////////////////////////////////////////////
551 void
552 FGMultiplayMgr::ProcessPosMsg(const char *Msg, netAddress & SenderAddress,
553                               unsigned len, long stamp)
554 {
555   T_MsgHdr* MsgHdr = (T_MsgHdr *)Msg;
556   if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + sizeof(T_PositionMsg)) {
557     SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
558             << "Position message received with insufficient data" );
559     return;
560   }
561   T_PositionMsg* PosMsg = (T_PositionMsg *)(Msg + sizeof(T_MsgHdr));
562   FGExternalMotionData motionInfo;
563   motionInfo.time = XDR_decode_double(PosMsg->time);
564   motionInfo.lag = XDR_decode_double(PosMsg->lag);
565   for (unsigned i = 0; i < 3; ++i)
566     motionInfo.position(i) = XDR_decode_double(PosMsg->position[i]);
567   SGVec3f angleAxis;
568   for (unsigned i = 0; i < 3; ++i)
569     angleAxis(i) = XDR_decode_float(PosMsg->orientation[i]);
570   motionInfo.orientation = SGQuatf::fromAngleAxis(angleAxis);
571   for (unsigned i = 0; i < 3; ++i)
572     motionInfo.linearVel(i) = XDR_decode_float(PosMsg->linearVel[i]);
573   for (unsigned i = 0; i < 3; ++i)
574     motionInfo.angularVel(i) = XDR_decode_float(PosMsg->angularVel[i]);
575   for (unsigned i = 0; i < 3; ++i)
576     motionInfo.linearAccel(i) = XDR_decode_float(PosMsg->linearAccel[i]);
577   for (unsigned i = 0; i < 3; ++i)
578     motionInfo.angularAccel(i) = XDR_decode_float(PosMsg->angularAccel[i]);
579
580
581   //cout << "INPUT MESSAGE\n";
582   xdr_data_t* xdr = (xdr_data_t*) 
583                    (Msg + sizeof(T_MsgHdr) + sizeof(T_PositionMsg));
584   while ((char*)xdr < Msg + len) {
585     FGPropertyData* pData = new FGPropertyData;
586     SGPropertyNode::Type type = SGPropertyNode::UNSPECIFIED;
587     
588     // First element is always the ID
589     pData->id = XDR_decode_uint32(*xdr);
590     //cout << pData->id << " ";
591     xdr++;
592     
593     // Check the ID actually exists and get the type
594     unsigned i = 0;
595     bool found = false;
596     while (FGMultiplayMgr::sIdPropertyList[i].name) 
597     {
598       if (sIdPropertyList[i].id == pData->id)
599       {
600         found = true;
601         pData->type = sIdPropertyList[i].type;
602       } 
603       
604       i++;
605     }
606     
607     if (found == true)
608     {
609       // How we decode the remainder of the property depends on the type
610       switch (pData->type) {
611         case SGPropertyNode::INT:        
612         case SGPropertyNode::BOOL:
613         case SGPropertyNode::LONG:        
614           pData->int_value = XDR_decode_uint32(*xdr);
615           xdr++;
616           //cout << pData->int_value << "\n";
617           break;
618         case SGPropertyNode::FLOAT:
619         case SGPropertyNode::DOUBLE:
620           pData->float_value = XDR_decode_float(*xdr);
621           xdr++;
622           //cout << pData->float_value << "\n";
623           break;
624         case SGPropertyNode::STRING:
625         case SGPropertyNode::UNSPECIFIED:
626           {
627             // String is complicated. It consists of
628             // The length of the string
629             // The string itself
630             // Padding to the nearest 4-bytes.    
631             uint32_t length = XDR_decode_uint32(*xdr);
632             xdr++;
633             //cout << length << " ";
634
635             if ((length > 0) && (length < MAX_TEXT_SIZE))
636             {
637               pData->string_value = new char[length + 1];
638               //cout << " String: ";
639
640               for (int i = 0; i < length; i++)
641               {
642                 pData->string_value[i] = (char) XDR_decode_int8(*xdr);
643                 xdr++;
644                 //cout << pData->string_value[i];
645               }
646
647               pData->string_value[length] = '\0';
648
649               // Now handle the padding
650               while ((length % 4) != 0)
651               {
652                 xdr++;
653                 length++;
654                 //cout << "0";
655               }
656             }
657             else
658             {
659               pData->string_value = new char[1];
660               pData->string_value[0] = '\0';
661             }
662
663             //cout << "\n";
664           }
665           break;
666
667         default:
668           pData->float_value = XDR_decode_float(*xdr);
669           cerr << "Unknown Prop type " << pData->id << " " << pData->type << "\n";
670           xdr++;
671           break;
672       }            
673
674       motionInfo.properties.push_back(pData);
675     }
676     else
677     {
678       // We failed to find the property. We'll try the next packet immediately.
679       //cout << " Unknown\n";
680     }
681   }
682   
683   FGAIMultiplayer* mp = getMultiplayer(MsgHdr->Callsign);
684   if (!mp)
685     mp = addMultiplayer(MsgHdr->Callsign, PosMsg->Model);
686   mp->addMotionInfo(motionInfo, stamp);
687 } // FGMultiplayMgr::ProcessPosMsg()
688 //////////////////////////////////////////////////////////////////////
689
690 //////////////////////////////////////////////////////////////////////
691 //
692 //  handle a chat message
693 //  FIXME: display chat message withi flightgear
694 //
695 //////////////////////////////////////////////////////////////////////
696 void
697 FGMultiplayMgr::ProcessChatMsg(const char *Msg, netAddress& SenderAddress)
698 {
699   T_MsgHdr* MsgHdr = (T_MsgHdr *)Msg;
700   if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + 1) {
701     SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
702             << "Chat message received with insufficient data" );
703     return;
704   }
705   
706   char *MsgBuf = new char[MsgHdr->MsgLen - sizeof(T_MsgHdr)];
707   strncpy(MsgBuf, ((T_ChatMsg *)(Msg + sizeof(T_MsgHdr)))->Text,
708           MsgHdr->MsgLen - sizeof(T_MsgHdr));
709   MsgBuf[MsgHdr->MsgLen - sizeof(T_MsgHdr) - 1] = '\0';
710   
711   T_ChatMsg* ChatMsg = (T_ChatMsg *)(Msg + sizeof(T_MsgHdr));
712   SG_LOG ( SG_NETWORK, SG_ALERT, "Chat [" << MsgHdr->Callsign << "]"
713            << " " << MsgBuf << endl);
714
715   delete [] MsgBuf;
716 } // FGMultiplayMgr::ProcessChatMsg ()
717 //////////////////////////////////////////////////////////////////////
718
719 void
720 FGMultiplayMgr::FillMsgHdr(T_MsgHdr *MsgHdr, int MsgId, unsigned _len)
721 {
722   uint32_t len;
723   switch (MsgId) {
724   case CHAT_MSG_ID:
725     len = sizeof(T_MsgHdr) + sizeof(T_ChatMsg);
726     break;
727   case POS_DATA_ID:
728     len = _len;
729     break;
730   default:
731     len = sizeof(T_MsgHdr);
732     break;
733   }
734   MsgHdr->Magic           = XDR_encode_uint32(MSG_MAGIC);
735   MsgHdr->Version         = XDR_encode_uint32(PROTO_VER);
736   MsgHdr->MsgId           = XDR_encode_uint32(MsgId);
737   MsgHdr->MsgLen          = XDR_encode_uint32(len);
738   MsgHdr->ReplyAddress    = 0; // Are obsolete, keep them for the server for
739   MsgHdr->ReplyPort       = 0; // now
740   strncpy(MsgHdr->Callsign, mCallsign.c_str(), MAX_CALLSIGN_LEN);
741   MsgHdr->Callsign[MAX_CALLSIGN_LEN - 1] = '\0';
742 }
743
744 FGAIMultiplayer*
745 FGMultiplayMgr::addMultiplayer(const std::string& callsign,
746                                const std::string& modelName)
747 {
748   if (0 < mMultiPlayerMap.count(callsign))
749     return mMultiPlayerMap[callsign];
750
751   FGAIMultiplayer* mp = new FGAIMultiplayer;
752   mp->setPath(modelName.c_str());
753   mp->setCallSign(callsign);
754   mMultiPlayerMap[callsign] = mp;
755
756   FGAIManager *aiMgr = (FGAIManager*)globals->get_subsystem("ai_model");
757   if (aiMgr) {
758     aiMgr->attach(mp);
759
760     /// FIXME: that must follow the attach ATM ...
761     unsigned i = 0;
762     while (sIdPropertyList[i].name) {
763       mp->addPropertyId(sIdPropertyList[i].id, sIdPropertyList[i].name);
764       ++i;
765     }
766   }
767
768   return mp;
769 }
770
771 FGAIMultiplayer*
772 FGMultiplayMgr::getMultiplayer(const std::string& callsign)
773 {
774   if (0 < mMultiPlayerMap.count(callsign))
775     return mMultiPlayerMap[callsign];
776   else
777     return 0;
778 }