]> git.mxchange.org Git - flightgear.git/blob - src/MultiPlayer/multiplaymgr.cxx
Canvas: update for new bounding box getters.
[flightgear.git] / src / MultiPlayer / multiplaymgr.cxx
1 //////////////////////////////////////////////////////////////////////
2 //
3 // multiplaymgr.cxx
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 <iostream>
35 #include <algorithm>
36 #include <cstring>
37 #include <errno.h>
38
39 #include <simgear/misc/stdint.hxx>
40 #include <simgear/timing/timestamp.hxx>
41 #include <simgear/debug/logstream.hxx>
42 #include <simgear/props/props.hxx>
43
44 #include <AIModel/AIManager.hxx>
45 #include <AIModel/AIMultiplayer.hxx>
46 #include <Main/fg_props.hxx>
47 #include "multiplaymgr.hxx"
48 #include "mpmessages.hxx"
49 #include <FDM/flightProperties.hxx>
50
51 using namespace std;
52
53 #define MAX_PACKET_SIZE 1200
54 #define MAX_TEXT_SIZE 128
55
56 struct IdPropertyList {
57   unsigned id;
58   const char* name;
59   simgear::props::Type type;
60 };
61  
62 static const IdPropertyList* findProperty(unsigned id);
63   
64 // A static map of protocol property id values to property paths,
65 // This should be extendable dynamically for every specific aircraft ...
66 // For now only that static list
67 static const IdPropertyList sIdPropertyList[] = {
68   {100, "surface-positions/left-aileron-pos-norm",  simgear::props::FLOAT},
69   {101, "surface-positions/right-aileron-pos-norm", simgear::props::FLOAT},
70   {102, "surface-positions/elevator-pos-norm",      simgear::props::FLOAT},
71   {103, "surface-positions/rudder-pos-norm",        simgear::props::FLOAT},
72   {104, "surface-positions/flap-pos-norm",          simgear::props::FLOAT},
73   {105, "surface-positions/speedbrake-pos-norm",    simgear::props::FLOAT},
74   {106, "gear/tailhook/position-norm",              simgear::props::FLOAT},
75   {107, "gear/launchbar/position-norm",             simgear::props::FLOAT},
76   {108, "gear/launchbar/state",                     simgear::props::STRING},
77   {109, "gear/launchbar/holdback-position-norm",    simgear::props::FLOAT},
78   {110, "canopy/position-norm",                     simgear::props::FLOAT},
79   {111, "surface-positions/wing-pos-norm",          simgear::props::FLOAT},
80   {112, "surface-positions/wing-fold-pos-norm",     simgear::props::FLOAT},
81
82   {200, "gear/gear[0]/compression-norm",           simgear::props::FLOAT},
83   {201, "gear/gear[0]/position-norm",              simgear::props::FLOAT},
84   {210, "gear/gear[1]/compression-norm",           simgear::props::FLOAT},
85   {211, "gear/gear[1]/position-norm",              simgear::props::FLOAT},
86   {220, "gear/gear[2]/compression-norm",           simgear::props::FLOAT},
87   {221, "gear/gear[2]/position-norm",              simgear::props::FLOAT},
88   {230, "gear/gear[3]/compression-norm",           simgear::props::FLOAT},
89   {231, "gear/gear[3]/position-norm",              simgear::props::FLOAT},
90   {240, "gear/gear[4]/compression-norm",           simgear::props::FLOAT},
91   {241, "gear/gear[4]/position-norm",              simgear::props::FLOAT},
92
93   {300, "engines/engine[0]/n1",  simgear::props::FLOAT},
94   {301, "engines/engine[0]/n2",  simgear::props::FLOAT},
95   {302, "engines/engine[0]/rpm", simgear::props::FLOAT},
96   {310, "engines/engine[1]/n1",  simgear::props::FLOAT},
97   {311, "engines/engine[1]/n2",  simgear::props::FLOAT},
98   {312, "engines/engine[1]/rpm", simgear::props::FLOAT},
99   {320, "engines/engine[2]/n1",  simgear::props::FLOAT},
100   {321, "engines/engine[2]/n2",  simgear::props::FLOAT},
101   {322, "engines/engine[2]/rpm", simgear::props::FLOAT},
102   {330, "engines/engine[3]/n1",  simgear::props::FLOAT},
103   {331, "engines/engine[3]/n2",  simgear::props::FLOAT},
104   {332, "engines/engine[3]/rpm", simgear::props::FLOAT},
105   {340, "engines/engine[4]/n1",  simgear::props::FLOAT},
106   {341, "engines/engine[4]/n2",  simgear::props::FLOAT},
107   {342, "engines/engine[4]/rpm", simgear::props::FLOAT},
108   {350, "engines/engine[5]/n1",  simgear::props::FLOAT},
109   {351, "engines/engine[5]/n2",  simgear::props::FLOAT},
110   {352, "engines/engine[5]/rpm", simgear::props::FLOAT},
111   {360, "engines/engine[6]/n1",  simgear::props::FLOAT},
112   {361, "engines/engine[6]/n2",  simgear::props::FLOAT},
113   {362, "engines/engine[6]/rpm", simgear::props::FLOAT},
114   {370, "engines/engine[7]/n1",  simgear::props::FLOAT},
115   {371, "engines/engine[7]/n2",  simgear::props::FLOAT},
116   {372, "engines/engine[7]/rpm", simgear::props::FLOAT},
117   {380, "engines/engine[8]/n1",  simgear::props::FLOAT},
118   {381, "engines/engine[8]/n2",  simgear::props::FLOAT},
119   {382, "engines/engine[8]/rpm", simgear::props::FLOAT},
120   {390, "engines/engine[9]/n1",  simgear::props::FLOAT},
121   {391, "engines/engine[9]/n2",  simgear::props::FLOAT},
122   {392, "engines/engine[9]/rpm", simgear::props::FLOAT},
123
124   {800, "rotors/main/rpm", simgear::props::FLOAT},
125   {801, "rotors/tail/rpm", simgear::props::FLOAT},
126   {810, "rotors/main/blade[0]/position-deg",  simgear::props::FLOAT},
127   {811, "rotors/main/blade[1]/position-deg",  simgear::props::FLOAT},
128   {812, "rotors/main/blade[2]/position-deg",  simgear::props::FLOAT},
129   {813, "rotors/main/blade[3]/position-deg",  simgear::props::FLOAT},
130   {820, "rotors/main/blade[0]/flap-deg",  simgear::props::FLOAT},
131   {821, "rotors/main/blade[1]/flap-deg",  simgear::props::FLOAT},
132   {822, "rotors/main/blade[2]/flap-deg",  simgear::props::FLOAT},
133   {823, "rotors/main/blade[3]/flap-deg",  simgear::props::FLOAT},
134   {830, "rotors/tail/blade[0]/position-deg",  simgear::props::FLOAT},
135   {831, "rotors/tail/blade[1]/position-deg",  simgear::props::FLOAT},
136
137   {900, "sim/hitches/aerotow/tow/length",                       simgear::props::FLOAT},
138   {901, "sim/hitches/aerotow/tow/elastic-constant",             simgear::props::FLOAT},
139   {902, "sim/hitches/aerotow/tow/weight-per-m-kg-m",            simgear::props::FLOAT},
140   {903, "sim/hitches/aerotow/tow/dist",                         simgear::props::FLOAT},
141   {904, "sim/hitches/aerotow/tow/connected-to-property-node",   simgear::props::BOOL},
142   {905, "sim/hitches/aerotow/tow/connected-to-ai-or-mp-callsign",   simgear::props::STRING},
143   {906, "sim/hitches/aerotow/tow/brake-force",                  simgear::props::FLOAT},
144   {907, "sim/hitches/aerotow/tow/end-force-x",                  simgear::props::FLOAT},
145   {908, "sim/hitches/aerotow/tow/end-force-y",                  simgear::props::FLOAT},
146   {909, "sim/hitches/aerotow/tow/end-force-z",                  simgear::props::FLOAT},
147   {930, "sim/hitches/aerotow/is-slave",                         simgear::props::BOOL},
148   {931, "sim/hitches/aerotow/speed-in-tow-direction",           simgear::props::FLOAT},
149   {932, "sim/hitches/aerotow/open",                             simgear::props::BOOL},
150   {933, "sim/hitches/aerotow/local-pos-x",                      simgear::props::FLOAT},
151   {934, "sim/hitches/aerotow/local-pos-y",                      simgear::props::FLOAT},
152   {935, "sim/hitches/aerotow/local-pos-z",                      simgear::props::FLOAT},
153
154   {1001, "controls/flight/slats",  simgear::props::FLOAT},
155   {1002, "controls/flight/speedbrake",  simgear::props::FLOAT},
156   {1003, "controls/flight/spoilers",  simgear::props::FLOAT},
157   {1004, "controls/gear/gear-down",  simgear::props::FLOAT},
158   {1005, "controls/lighting/nav-lights",  simgear::props::FLOAT},
159   {1006, "controls/armament/station[0]/jettison-all",  simgear::props::BOOL},
160
161   {1100, "sim/model/variant", simgear::props::INT},
162   {1101, "sim/model/livery/file", simgear::props::STRING},
163
164   {1200, "environment/wildfire/data", simgear::props::STRING},
165   {1201, "environment/contrail", simgear::props::INT},
166
167   {1300, "tanker", simgear::props::INT},
168
169   {1400, "scenery/events", simgear::props::STRING},
170
171   {1500, "instrumentation/transponder/transmitted-id", simgear::props::INT},
172   {1501, "instrumentation/transponder/altitude", simgear::props::INT},
173   {1502, "instrumentation/transponder/ident", simgear::props::BOOL},
174   {1503, "instrumentation/transponder/inputs/mode", simgear::props::INT},
175
176   {10001, "sim/multiplay/transmission-freq-hz",  simgear::props::STRING},
177   {10002, "sim/multiplay/chat",  simgear::props::STRING},
178
179   {10100, "sim/multiplay/generic/string[0]", simgear::props::STRING},
180   {10101, "sim/multiplay/generic/string[1]", simgear::props::STRING},
181   {10102, "sim/multiplay/generic/string[2]", simgear::props::STRING},
182   {10103, "sim/multiplay/generic/string[3]", simgear::props::STRING},
183   {10104, "sim/multiplay/generic/string[4]", simgear::props::STRING},
184   {10105, "sim/multiplay/generic/string[5]", simgear::props::STRING},
185   {10106, "sim/multiplay/generic/string[6]", simgear::props::STRING},
186   {10107, "sim/multiplay/generic/string[7]", simgear::props::STRING},
187   {10108, "sim/multiplay/generic/string[8]", simgear::props::STRING},
188   {10109, "sim/multiplay/generic/string[9]", simgear::props::STRING},
189   {10110, "sim/multiplay/generic/string[10]", simgear::props::STRING},
190   {10111, "sim/multiplay/generic/string[11]", simgear::props::STRING},
191   {10112, "sim/multiplay/generic/string[12]", simgear::props::STRING},
192   {10113, "sim/multiplay/generic/string[13]", simgear::props::STRING},
193   {10114, "sim/multiplay/generic/string[14]", simgear::props::STRING},
194   {10115, "sim/multiplay/generic/string[15]", simgear::props::STRING},
195   {10116, "sim/multiplay/generic/string[16]", simgear::props::STRING},
196   {10117, "sim/multiplay/generic/string[17]", simgear::props::STRING},
197   {10118, "sim/multiplay/generic/string[18]", simgear::props::STRING},
198   {10119, "sim/multiplay/generic/string[19]", simgear::props::STRING},
199
200   {10200, "sim/multiplay/generic/float[0]", simgear::props::FLOAT},
201   {10201, "sim/multiplay/generic/float[1]", simgear::props::FLOAT},
202   {10202, "sim/multiplay/generic/float[2]", simgear::props::FLOAT},
203   {10203, "sim/multiplay/generic/float[3]", simgear::props::FLOAT},
204   {10204, "sim/multiplay/generic/float[4]", simgear::props::FLOAT},
205   {10205, "sim/multiplay/generic/float[5]", simgear::props::FLOAT},
206   {10206, "sim/multiplay/generic/float[6]", simgear::props::FLOAT},
207   {10207, "sim/multiplay/generic/float[7]", simgear::props::FLOAT},
208   {10208, "sim/multiplay/generic/float[8]", simgear::props::FLOAT},
209   {10209, "sim/multiplay/generic/float[9]", simgear::props::FLOAT},
210   {10210, "sim/multiplay/generic/float[10]", simgear::props::FLOAT},
211   {10211, "sim/multiplay/generic/float[11]", simgear::props::FLOAT},
212   {10212, "sim/multiplay/generic/float[12]", simgear::props::FLOAT},
213   {10213, "sim/multiplay/generic/float[13]", simgear::props::FLOAT},
214   {10214, "sim/multiplay/generic/float[14]", simgear::props::FLOAT},
215   {10215, "sim/multiplay/generic/float[15]", simgear::props::FLOAT},
216   {10216, "sim/multiplay/generic/float[16]", simgear::props::FLOAT},
217   {10217, "sim/multiplay/generic/float[17]", simgear::props::FLOAT},
218   {10218, "sim/multiplay/generic/float[18]", simgear::props::FLOAT},
219   {10219, "sim/multiplay/generic/float[19]", simgear::props::FLOAT},
220
221   {10300, "sim/multiplay/generic/int[0]", simgear::props::INT},
222   {10301, "sim/multiplay/generic/int[1]", simgear::props::INT},
223   {10302, "sim/multiplay/generic/int[2]", simgear::props::INT},
224   {10303, "sim/multiplay/generic/int[3]", simgear::props::INT},
225   {10304, "sim/multiplay/generic/int[4]", simgear::props::INT},
226   {10305, "sim/multiplay/generic/int[5]", simgear::props::INT},
227   {10306, "sim/multiplay/generic/int[6]", simgear::props::INT},
228   {10307, "sim/multiplay/generic/int[7]", simgear::props::INT},
229   {10308, "sim/multiplay/generic/int[8]", simgear::props::INT},
230   {10309, "sim/multiplay/generic/int[9]", simgear::props::INT},
231   {10310, "sim/multiplay/generic/int[10]", simgear::props::INT},
232   {10311, "sim/multiplay/generic/int[11]", simgear::props::INT},
233   {10312, "sim/multiplay/generic/int[12]", simgear::props::INT},
234   {10313, "sim/multiplay/generic/int[13]", simgear::props::INT},
235   {10314, "sim/multiplay/generic/int[14]", simgear::props::INT},
236   {10315, "sim/multiplay/generic/int[15]", simgear::props::INT},
237   {10316, "sim/multiplay/generic/int[16]", simgear::props::INT},
238   {10317, "sim/multiplay/generic/int[17]", simgear::props::INT},
239   {10318, "sim/multiplay/generic/int[18]", simgear::props::INT},
240   {10319, "sim/multiplay/generic/int[19]", simgear::props::INT}
241 };
242
243 const unsigned int numProperties = (sizeof(sIdPropertyList)
244                                  / sizeof(sIdPropertyList[0]));
245
246 // Look up a property ID using binary search.
247 namespace
248 {
249   struct ComparePropertyId
250   {
251     bool operator()(const IdPropertyList& lhs,
252                     const IdPropertyList& rhs)
253     {
254       return lhs.id < rhs.id;
255     }
256     bool operator()(const IdPropertyList& lhs,
257                     unsigned id)
258     {
259       return lhs.id < id;
260     }
261     bool operator()(unsigned id,
262                     const IdPropertyList& rhs)
263     {
264       return id < rhs.id;
265     }
266   };    
267 }
268
269 const IdPropertyList* findProperty(unsigned id)
270 {
271   std::pair<const IdPropertyList*, const IdPropertyList*> result
272     = std::equal_range(sIdPropertyList, sIdPropertyList + numProperties, id,
273                        ComparePropertyId());
274   if (result.first == result.second) {
275     return 0;
276   } else {
277     return result.first;
278   }
279 }
280
281 namespace
282 {
283   bool verifyProperties(const xdr_data_t* data, const xdr_data_t* end)
284   {
285     using namespace simgear;
286     const xdr_data_t* xdr = data;
287     while (xdr < end) {
288       unsigned id = XDR_decode_uint32(*xdr);
289       const IdPropertyList* plist = findProperty(id);
290     
291       if (plist) {
292         xdr++;
293         // How we decode the remainder of the property depends on the type
294         switch (plist->type) {
295         case props::INT:
296         case props::BOOL:
297         case props::LONG:
298           xdr++;
299           break;
300         case props::FLOAT:
301         case props::DOUBLE:
302           {
303             float val = XDR_decode_float(*xdr);
304             if (SGMisc<float>::isNaN(val))
305               return false;
306             xdr++;
307             break;
308           }
309         case props::STRING:
310         case props::UNSPECIFIED:
311           {
312             // String is complicated. It consists of
313             // The length of the string
314             // The string itself
315             // Padding to the nearest 4-bytes.
316             // XXX Yes, each byte is padded out to a word! Too late
317             // to change...
318             uint32_t length = XDR_decode_uint32(*xdr);
319             xdr++;
320             // Old versions truncated the string but left the length
321             // unadjusted.
322             if (length > MAX_TEXT_SIZE)
323               length = MAX_TEXT_SIZE;
324             xdr += length;
325             // Now handle the padding
326             while ((length % 4) != 0)
327               {
328                 xdr++;
329                 length++;
330                 //cout << "0";
331               }
332           }
333           break;
334         default:
335           // cerr << "Unknown Prop type " << id << " " << type << "\n";
336           xdr++;
337           break;
338         }            
339       }
340       else {
341         // give up; this is a malformed property list.
342         return false;
343       }
344     }
345     return true;
346   }
347 }
348
349 class MPPropertyListener : public SGPropertyChangeListener
350 {
351 public:
352   MPPropertyListener(FGMultiplayMgr* mp) :
353     _multiplay(mp)
354   {
355   }
356
357   virtual void childAdded(SGPropertyNode*, SGPropertyNode*)
358   {
359     _multiplay->setPropertiesChanged();
360   }
361
362 private:
363   FGMultiplayMgr* _multiplay;
364 };
365
366 //////////////////////////////////////////////////////////////////////
367 //
368 //  MultiplayMgr constructor
369 //
370 //////////////////////////////////////////////////////////////////////
371 FGMultiplayMgr::FGMultiplayMgr() 
372 {
373   mInitialised   = false;
374   mHaveServer    = false;
375   mListener = NULL;
376 } // FGMultiplayMgr::FGMultiplayMgr()
377 //////////////////////////////////////////////////////////////////////
378
379 //////////////////////////////////////////////////////////////////////
380 //
381 //  MultiplayMgr destructor
382 //
383 //////////////////////////////////////////////////////////////////////
384 FGMultiplayMgr::~FGMultiplayMgr() 
385 {
386   
387 } // FGMultiplayMgr::~FGMultiplayMgr()
388 //////////////////////////////////////////////////////////////////////
389
390 //////////////////////////////////////////////////////////////////////
391 //
392 //  Initialise object
393 //
394 //////////////////////////////////////////////////////////////////////
395 void
396 FGMultiplayMgr::init (void) 
397 {
398   //////////////////////////////////////////////////
399   //  Initialise object if not already done
400   //////////////////////////////////////////////////
401   if (mInitialised) {
402     SG_LOG(SG_NETWORK, SG_WARN, "FGMultiplayMgr::init - already initialised");
403     return;
404   }
405
406   SGPropertyNode* propOnline = fgGetNode("/sim/multiplay/online", true);
407   propOnline->setBoolValue(false);
408   propOnline->setAttribute(SGPropertyNode::PRESERVE, true);
409
410   //////////////////////////////////////////////////
411   //  Set members from property values
412   //////////////////////////////////////////////////
413   short rxPort = fgGetInt("/sim/multiplay/rxport");
414   string rxAddress = fgGetString("/sim/multiplay/rxhost");
415   short txPort = fgGetInt("/sim/multiplay/txport", 5000);
416   string txAddress = fgGetString("/sim/multiplay/txhost");
417   
418   int hz = fgGetInt("/sim/multiplay/tx-rate-hz", 10);
419   if (hz < 1) {
420     hz = 10;
421   }
422   
423   mDt = 1.0 / hz;
424   mTimeUntilSend = 0.0;
425   
426   mCallsign = fgGetString("/sim/multiplay/callsign");
427   fgGetNode("/sim/multiplay/callsign", true)->setAttribute(SGPropertyNode::PRESERVE, true);
428     
429   if ((!txAddress.empty()) && (txAddress!="0")) {
430     mServer.set(txAddress.c_str(), txPort);
431     if (strncmp (mServer.getHost(), "0.0.0.0", 8) == 0) {
432       mHaveServer = false;
433       SG_LOG(SG_NETWORK, SG_ALERT,
434         "Cannot enable multiplayer mode: resolving MP server address '"
435         << txAddress << "' failed.");
436       return;
437     } else {
438       SG_LOG(SG_NETWORK, SG_INFO, "FGMultiplayMgr - have server");
439       mHaveServer = true;
440     }
441     if (rxPort <= 0)
442       rxPort = txPort;
443   } else {
444     SG_LOG(SG_NETWORK, SG_INFO, "FGMultiplayMgr - multiplayer mode disabled (no MP server specificed).");
445     return;
446   }
447
448   if (rxPort <= 0) {
449     SG_LOG(SG_NETWORK, SG_ALERT,
450       "Cannot enable multiplayer mode: No receiver port specified.");
451     return;
452   }
453   if (mCallsign.empty())
454     mCallsign = "JohnDoe"; // FIXME: use getpwuid
455   SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-txaddress= "<<txAddress);
456   SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-txport= "<<txPort );
457   SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-rxaddress="<<rxAddress );
458   SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-rxport= "<<rxPort);
459   SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-callsign= "<<mCallsign);
460   
461   mSocket.reset(new simgear::Socket());
462   if (!mSocket->open(false)) {
463     SG_LOG( SG_NETWORK, SG_ALERT,
464             "Cannot enable multiplayer mode: creating data socket failed." );
465     return;
466   }
467   mSocket->setBlocking(false);
468   if (mSocket->bind(rxAddress.c_str(), rxPort) != 0) {
469     SG_LOG( SG_NETWORK, SG_ALERT,
470             "Cannot enable multiplayer mode: binding receive socket failed. "
471             << strerror(errno) << "(errno " << errno << ")");
472     return;
473   }
474   
475   mPropertiesChanged = true;
476   mListener = new MPPropertyListener(this);
477   globals->get_props()->addChangeListener(mListener, false);
478   
479   fgSetBool("/sim/multiplay/online", true);
480   mInitialised = true;
481
482   SG_LOG(SG_NETWORK, SG_ALERT, "Multiplayer mode active!");
483
484   if (!fgGetBool("/sim/ai/enabled"))
485   {
486       // multiplayer depends on AI module
487       fgSetBool("/sim/ai/enabled", true);
488   }
489 } // FGMultiplayMgr::init()
490 //////////////////////////////////////////////////////////////////////
491
492 //////////////////////////////////////////////////////////////////////
493 //
494 //  Closes and deletes the local player object. Closes
495 //  and deletes the tx socket. Resets the object state to unitialised.
496 //
497 //////////////////////////////////////////////////////////////////////
498 void
499 FGMultiplayMgr::shutdown (void) 
500 {
501   fgSetBool("/sim/multiplay/online", false);
502   
503   if (mSocket.get()) {
504     mSocket->close();
505     mSocket.reset(); 
506   }
507   
508   MultiPlayerMap::iterator it = mMultiPlayerMap.begin(),
509     end = mMultiPlayerMap.end();
510   for (; it != end; ++it) {
511     it->second->setDie(true);
512   }
513   mMultiPlayerMap.clear();
514   
515   if (mListener) {
516     globals->get_props()->removeChangeListener(mListener);
517     delete mListener;
518     mListener = NULL;
519   }
520   
521   mInitialised = false;
522 } // FGMultiplayMgr::Close(void)
523 //////////////////////////////////////////////////////////////////////
524
525 void
526 FGMultiplayMgr::reinit()
527 {
528   shutdown();
529   init();
530 }
531
532 //////////////////////////////////////////////////////////////////////
533 //
534 //  Description: Sends the position data for the local position.
535 //
536 //////////////////////////////////////////////////////////////////////
537
538 /**
539  * The buffer that holds a multi-player message, suitably aligned.
540  */
541 union FGMultiplayMgr::MsgBuf
542 {
543     MsgBuf()
544     {
545         memset(&Msg, 0, sizeof(Msg));
546     }
547
548     T_MsgHdr* msgHdr()
549     {
550         return &Header;
551     }
552
553     const T_MsgHdr* msgHdr() const
554     {
555         return reinterpret_cast<const T_MsgHdr*>(&Header);
556     }
557
558     T_PositionMsg* posMsg()
559     {
560         return reinterpret_cast<T_PositionMsg*>(Msg + sizeof(T_MsgHdr));
561     }
562
563     const T_PositionMsg* posMsg() const
564     {
565         return reinterpret_cast<const T_PositionMsg*>(Msg + sizeof(T_MsgHdr));
566     }
567
568     xdr_data_t* properties()
569     {
570         return reinterpret_cast<xdr_data_t*>(Msg + sizeof(T_MsgHdr)
571                                              + sizeof(T_PositionMsg));
572     }
573
574     const xdr_data_t* properties() const
575     {
576         return reinterpret_cast<const xdr_data_t*>(Msg + sizeof(T_MsgHdr)
577                                                    + sizeof(T_PositionMsg));
578     }
579     /**
580      * The end of the properties buffer.
581      */
582     xdr_data_t* propsEnd()
583     {
584         return reinterpret_cast<xdr_data_t*>(Msg + MAX_PACKET_SIZE);
585     };
586
587     const xdr_data_t* propsEnd() const
588     {
589         return reinterpret_cast<const xdr_data_t*>(Msg + MAX_PACKET_SIZE);
590     };
591     /**
592      * The end of properties actually in the buffer. This assumes that
593      * the message header is valid.
594      */
595     xdr_data_t* propsRecvdEnd()
596     {
597         return reinterpret_cast<xdr_data_t*>(Msg + Header.MsgLen);
598     }
599
600     const xdr_data_t* propsRecvdEnd() const
601     {
602         return reinterpret_cast<const xdr_data_t*>(Msg + Header.MsgLen);
603     }
604     
605     xdr_data2_t double_val;
606     char Msg[MAX_PACKET_SIZE];
607     T_MsgHdr Header;
608 };
609
610 bool
611 FGMultiplayMgr::isSane(const FGExternalMotionData& motionInfo)
612 {
613     // check for corrupted data (NaNs)
614     bool isCorrupted = false;
615     isCorrupted |= ((SGMisc<double>::isNaN(motionInfo.time           )) ||
616                     (SGMisc<double>::isNaN(motionInfo.lag            )) ||
617                     (osg::isNaN(motionInfo.orientation(3) )));
618     for (unsigned i = 0; (i < 3)&&(!isCorrupted); ++i)
619     {
620         isCorrupted |= ((osg::isNaN(motionInfo.position(i)      ))||
621                         (osg::isNaN(motionInfo.orientation(i)   ))||
622                         (osg::isNaN(motionInfo.linearVel(i))    )||
623                         (osg::isNaN(motionInfo.angularVel(i))   )||
624                         (osg::isNaN(motionInfo.linearAccel(i))  )||
625                         (osg::isNaN(motionInfo.angularAccel(i)) ));
626     }
627     return !isCorrupted;
628 }
629
630 void
631 FGMultiplayMgr::SendMyPosition(const FGExternalMotionData& motionInfo)
632 {
633   if ((! mInitialised) || (! mHaveServer))
634         return;
635
636   if (! mHaveServer) {
637       SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::SendMyPosition - no server");
638       return;
639   }
640
641   if (!isSane(motionInfo))
642   {
643       // Current local data is invalid (NaN), so stop MP transmission.
644       // => Be nice to older FG versions (no NaN checks) and don't waste bandwidth.
645       SG_LOG(SG_NETWORK, SG_ALERT, "FGMultiplayMgr::SendMyPosition - "
646               << "Local data is invalid (NaN). Data not transmitted.");
647       return;
648   }
649
650   static MsgBuf msgBuf;
651   static unsigned msgLen = 0;
652   T_PositionMsg* PosMsg = msgBuf.posMsg();
653
654   strncpy(PosMsg->Model, fgGetString("/sim/model/path"), MAX_MODEL_NAME_LEN);
655   PosMsg->Model[MAX_MODEL_NAME_LEN - 1] = '\0';
656   if (fgGetBool("/sim/freeze/replay-state", true)&&
657       fgGetBool("/sim/multiplay/freeze-on-replay",true))
658   {
659       // do not send position updates during replay
660       for (unsigned i = 0 ; i < 3; ++i)
661       {
662           // no movement during replay
663           PosMsg->linearVel[i] = XDR_encode_float (0.0);
664           PosMsg->angularVel[i] = XDR_encode_float (0.0);
665           PosMsg->linearAccel[i] = XDR_encode_float (0.0);
666           PosMsg->angularAccel[i] = XDR_encode_float (0.0);
667       }
668       // all other data remains unchanged (resend last state) 
669   }
670   else
671   {
672       PosMsg->time = XDR_encode_double (motionInfo.time);
673       PosMsg->lag = XDR_encode_double (motionInfo.lag);
674       for (unsigned i = 0 ; i < 3; ++i)
675         PosMsg->position[i] = XDR_encode_double (motionInfo.position(i));
676       SGVec3f angleAxis;
677       motionInfo.orientation.getAngleAxis(angleAxis);
678       for (unsigned i = 0 ; i < 3; ++i)
679         PosMsg->orientation[i] = XDR_encode_float (angleAxis(i));
680       for (unsigned i = 0 ; i < 3; ++i)
681         PosMsg->linearVel[i] = XDR_encode_float (motionInfo.linearVel(i));
682       for (unsigned i = 0 ; i < 3; ++i)
683         PosMsg->angularVel[i] = XDR_encode_float (motionInfo.angularVel(i));
684       for (unsigned i = 0 ; i < 3; ++i)
685         PosMsg->linearAccel[i] = XDR_encode_float (motionInfo.linearAccel(i));
686       for (unsigned i = 0 ; i < 3; ++i)
687         PosMsg->angularAccel[i] = XDR_encode_float (motionInfo.angularAccel(i));
688
689       xdr_data_t* ptr = msgBuf.properties();
690       std::vector<FGPropertyData*>::const_iterator it;
691       it = motionInfo.properties.begin();
692       //cout << "OUTPUT PROPERTIES\n";
693       xdr_data_t* msgEnd = msgBuf.propsEnd();
694       while (it != motionInfo.properties.end() && ptr + 2 < msgEnd) {
695         
696         // First element is the ID. Write it out when we know we have room for
697         // the whole property.
698         xdr_data_t id =  XDR_encode_uint32((*it)->id);
699         // The actual data representation depends on the type
700         switch ((*it)->type) {
701           case simgear::props::INT:
702           case simgear::props::BOOL:
703           case simgear::props::LONG:
704             *ptr++ = id;
705             *ptr++ = XDR_encode_uint32((*it)->int_value);
706             //cout << "Prop:" << (*it)->id << " " << (*it)->type << " "<< (*it)->int_value << "\n";
707             break;
708           case simgear::props::FLOAT:
709           case simgear::props::DOUBLE:
710             *ptr++ = id;
711             *ptr++ = XDR_encode_float((*it)->float_value);
712             //cout << "Prop:" << (*it)->id << " " << (*it)->type << " "<< (*it)->float_value << "\n";
713             break;
714           case simgear::props::STRING:
715           case simgear::props::UNSPECIFIED:
716             {
717               // String is complicated. It consists of
718               // The length of the string
719               // The string itself
720               // Padding to the nearest 4-bytes.        
721               const char* lcharptr = (*it)->string_value;
722               
723               if (lcharptr != 0)
724               {
725                 // Add the length         
726                 ////cout << "String length: " << strlen(lcharptr) << "\n";
727                 uint32_t len = strlen(lcharptr);
728                 if (len > MAX_TEXT_SIZE)
729                   len = MAX_TEXT_SIZE;
730                 // XXX This should not be using 4 bytes per character!
731                 // If there's not enough room for this property, drop it
732                 // on the floor.
733                 if (ptr + 2 + ((len + 3) & ~3) > msgEnd)
734                     goto escape;
735                 //cout << "String length unint32: " << len << "\n";
736                 *ptr++ = id;
737                 *ptr++ = XDR_encode_uint32(len);
738                 if (len != 0)
739                 {
740                   // Now the text itself
741                   // XXX This should not be using 4 bytes per character!
742                   int lcount = 0;
743                   while ((*lcharptr != '\0') && (lcount < MAX_TEXT_SIZE)) 
744                   {
745                     *ptr++ = XDR_encode_int8(*lcharptr);
746                     lcharptr++;
747                     lcount++;          
748                   }
749     
750                   //cout << "Prop:" << (*it)->id << " " << (*it)->type << " " << len << " " << (*it)->string_value;
751     
752                   // Now pad if required
753                   while ((lcount % 4) != 0)
754                   {
755                     *ptr++ = XDR_encode_int8(0);
756                     lcount++;          
757                     //cout << "0";
758                   }
759                   
760                   //cout << "\n";
761                 }
762               }
763               else
764               {
765                 // Nothing to encode
766                 *ptr++ = id;
767                 *ptr++ = XDR_encode_uint32(0);
768                 //cout << "Prop:" << (*it)->id << " " << (*it)->type << " 0\n";
769               }
770             }
771             break;
772             
773           default:
774             //cout << " Unknown Type: " << (*it)->type << "\n";
775             *ptr++ = id;
776             *ptr++ = XDR_encode_float((*it)->float_value);;
777             //cout << "Prop:" << (*it)->id << " " << (*it)->type << " "<< (*it)->float_value << "\n";
778             break;
779         }
780             
781         ++it;
782       }
783   escape:
784       msgLen = reinterpret_cast<char*>(ptr) - msgBuf.Msg;
785       FillMsgHdr(msgBuf.msgHdr(), POS_DATA_ID, msgLen);
786   }
787   if (msgLen>0)
788       mSocket->sendto(msgBuf.Msg, msgLen, 0, &mServer);
789   SG_LOG(SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::SendMyPosition");
790 } // FGMultiplayMgr::SendMyPosition()
791
792 //////////////////////////////////////////////////////////////////////
793
794 //////////////////////////////////////////////////////////////////////
795 //
796 //  Name: SendTextMessage
797 //  Description: Sends a message to the player. The message must
798 //  contain a valid and correctly filled out header and optional
799 //  message body.
800 //
801 //////////////////////////////////////////////////////////////////////
802 void
803 FGMultiplayMgr::SendTextMessage(const string &MsgText)
804 {
805   if (!mInitialised || !mHaveServer)
806     return;
807
808   T_MsgHdr MsgHdr;
809   FillMsgHdr(&MsgHdr, CHAT_MSG_ID);
810   //////////////////////////////////////////////////
811   // Divide the text string into blocks that fit
812   // in the message and send the blocks.
813   //////////////////////////////////////////////////
814   unsigned iNextBlockPosition = 0;
815   T_ChatMsg ChatMsg;
816   
817   char Msg[sizeof(T_MsgHdr) + sizeof(T_ChatMsg)];
818   while (iNextBlockPosition < MsgText.length()) {
819     strncpy (ChatMsg.Text, 
820              MsgText.substr(iNextBlockPosition, MAX_CHAT_MSG_LEN - 1).c_str(),
821              MAX_CHAT_MSG_LEN);
822     ChatMsg.Text[MAX_CHAT_MSG_LEN - 1] = '\0';
823     memcpy (Msg, &MsgHdr, sizeof(T_MsgHdr));
824     memcpy (Msg + sizeof(T_MsgHdr), &ChatMsg, sizeof(T_ChatMsg));
825     mSocket->sendto (Msg, sizeof(T_MsgHdr) + sizeof(T_ChatMsg), 0, &mServer);
826     iNextBlockPosition += MAX_CHAT_MSG_LEN - 1;
827
828   }
829   
830   
831 } // FGMultiplayMgr::SendTextMessage ()
832 //////////////////////////////////////////////////////////////////////
833
834 //////////////////////////////////////////////////////////////////////
835 //
836 //  Name: ProcessData
837 //  Description: Processes data waiting at the receive socket. The
838 //  processing ends when there is no more data at the socket.
839 //  
840 //////////////////////////////////////////////////////////////////////
841 void
842 FGMultiplayMgr::update(double dt) 
843 {
844   if (!mInitialised)
845     return;
846
847   /// Just for expiry
848   long stamp = SGTimeStamp::now().getSeconds();
849
850   //////////////////////////////////////////////////
851   //  Send if required
852   //////////////////////////////////////////////////
853   mTimeUntilSend -= dt;
854   if (mTimeUntilSend <= 0.0) {
855     Send();
856   }
857
858   //////////////////////////////////////////////////
859   //  Read the receive socket and process any data
860   //////////////////////////////////////////////////
861   ssize_t bytes;
862   do {
863     MsgBuf msgBuf;
864     //////////////////////////////////////////////////
865     //  Although the recv call asks for 
866     //  MAX_PACKET_SIZE of data, the number of bytes
867     //  returned will only be that of the next
868     //  packet waiting to be processed.
869     //////////////////////////////////////////////////
870     simgear::IPAddress SenderAddress;
871     int RecvStatus = mSocket->recvfrom(msgBuf.Msg, sizeof(msgBuf.Msg), 0,
872                               &SenderAddress);
873     //////////////////////////////////////////////////
874     //  no Data received
875     //////////////////////////////////////////////////
876     if (RecvStatus == 0)
877         break;
878
879     // socket error reported?
880     // errno isn't thread-safe - so only check its value when
881     // socket return status < 0 really indicates a failure.
882     if ((RecvStatus < 0)&&
883         ((errno == EAGAIN) || (errno == 0))) // MSVC output "NoError" otherwise
884     {
885         // ignore "normal" errors
886         break;
887     }
888
889     if (RecvStatus<0)
890     {
891         SG_LOG(SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - Unable to receive data. "
892                << strerror(errno) << "(errno " << errno << ")");
893         break;
894     }
895
896     // status is positive: bytes received
897     bytes = (ssize_t) RecvStatus;
898     if (bytes <= static_cast<ssize_t>(sizeof(T_MsgHdr))) {
899       SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - "
900               << "received message with insufficient data" );
901       break;
902     }
903     //////////////////////////////////////////////////
904     //  Read header
905     //////////////////////////////////////////////////
906     T_MsgHdr* MsgHdr = msgBuf.msgHdr();
907     MsgHdr->Magic       = XDR_decode_uint32 (MsgHdr->Magic);
908     MsgHdr->Version     = XDR_decode_uint32 (MsgHdr->Version);
909     MsgHdr->MsgId       = XDR_decode_uint32 (MsgHdr->MsgId);
910     MsgHdr->MsgLen      = XDR_decode_uint32 (MsgHdr->MsgLen);
911     MsgHdr->ReplyPort   = XDR_decode_uint32 (MsgHdr->ReplyPort);
912     MsgHdr->Callsign[MAX_CALLSIGN_LEN -1] = '\0';
913     if (MsgHdr->Magic != MSG_MAGIC) {
914       SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - "
915               << "message has invalid magic number!" );
916       break;
917     }
918     if (MsgHdr->Version != PROTO_VER) {
919       SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - "
920               << "message has invalid protocol number!" );
921       break;
922     }
923     if (static_cast<ssize_t>(MsgHdr->MsgLen) != bytes) {
924       SG_LOG(SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - "
925              << "message from " << MsgHdr->Callsign << " has invalid length!");
926       break;
927     }
928     //////////////////////////////////////////////////
929     //  Process messages
930     //////////////////////////////////////////////////
931     switch (MsgHdr->MsgId) {
932     case CHAT_MSG_ID:
933       ProcessChatMsg(msgBuf, SenderAddress);
934       break;
935     case POS_DATA_ID:
936       ProcessPosMsg(msgBuf, SenderAddress, stamp);
937       break;
938     case UNUSABLE_POS_DATA_ID:
939     case OLD_OLD_POS_DATA_ID:
940     case OLD_PROP_MSG_ID:
941     case OLD_POS_DATA_ID:
942       break;
943     default:
944       SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - "
945               << "Unknown message Id received: " << MsgHdr->MsgId );
946       break;
947     }
948   } while (bytes > 0);
949
950   // check for expiry
951   MultiPlayerMap::iterator it = mMultiPlayerMap.begin();
952   while (it != mMultiPlayerMap.end()) {
953     if (it->second->getLastTimestamp() + 10 < stamp) {
954       std::string name = it->first;
955       it->second->setDie(true);
956       mMultiPlayerMap.erase(it);
957       it = mMultiPlayerMap.upper_bound(name);
958     } else
959       ++it;
960   }
961 } // FGMultiplayMgr::ProcessData(void)
962 //////////////////////////////////////////////////////////////////////
963
964 void
965 FGMultiplayMgr::Send()
966 {
967   using namespace simgear;
968   
969   findProperties();
970     
971   // smooth the send rate, by adjusting based on the 'remainder' time, which
972   // is how -ve mTimeUntilSend is. Watch for large values and ignore them,
973   // however.
974     if ((mTimeUntilSend < 0.0) && (fabs(mTimeUntilSend) < mDt)) {
975       mTimeUntilSend = mDt + mTimeUntilSend;
976     } else {
977       mTimeUntilSend = mDt;
978     }
979
980     double sim_time = globals->get_sim_time_sec();
981 //    static double lastTime = 0.0;
982     
983    // SG_LOG(SG_GENERAL, SG_INFO, "actual dt=" << sim_time - lastTime);
984 //    lastTime = sim_time;
985     
986     FlightProperties ifce;
987
988     // put together a motion info struct, you will get that later
989     // from FGInterface directly ...
990     FGExternalMotionData motionInfo;
991
992     // The current simulation time we need to update for,
993     // note that the simulation time is updated before calling all the
994     // update methods. Thus it contains the time intervals *end* time.
995     // The FDM is already run, so the states belong to that time.
996     motionInfo.time = sim_time;
997     motionInfo.lag = mDt;
998
999     // These are for now converted from lat/lon/alt and euler angles.
1000     // But this should change in FGInterface ...
1001     double lon = ifce.get_Longitude();
1002     double lat = ifce.get_Latitude();
1003     // first the aprioriate structure for the geodetic one
1004     SGGeod geod = SGGeod::fromRadFt(lon, lat, ifce.get_Altitude());
1005     // Convert to cartesion coordinate
1006     motionInfo.position = SGVec3d::fromGeod(geod);
1007     
1008     // The quaternion rotating from the earth centered frame to the
1009     // horizontal local frame
1010     SGQuatf qEc2Hl = SGQuatf::fromLonLatRad((float)lon, (float)lat);
1011     // The orientation wrt the horizontal local frame
1012     float heading = ifce.get_Psi();
1013     float pitch = ifce.get_Theta();
1014     float roll = ifce.get_Phi();
1015     SGQuatf hlOr = SGQuatf::fromYawPitchRoll(heading, pitch, roll);
1016     // The orientation of the vehicle wrt the earth centered frame
1017     motionInfo.orientation = qEc2Hl*hlOr;
1018
1019     if (!globals->get_subsystem("flight")->is_suspended()) {
1020       // velocities
1021       motionInfo.linearVel = SG_FEET_TO_METER*SGVec3f(ifce.get_uBody(),
1022                                                       ifce.get_vBody(),
1023                                                       ifce.get_wBody());
1024       motionInfo.angularVel = SGVec3f(ifce.get_P_body(),
1025                                       ifce.get_Q_body(),
1026                                       ifce.get_R_body());
1027       
1028       // accels, set that to zero for now.
1029       // Angular accelerations are missing from the interface anyway,
1030       // linear accelerations are screwed up at least for JSBSim.
1031 //  motionInfo.linearAccel = SG_FEET_TO_METER*SGVec3f(ifce.get_U_dot_body(),
1032 //                                                    ifce.get_V_dot_body(),
1033 //                                                    ifce.get_W_dot_body());
1034       motionInfo.linearAccel = SGVec3f::zeros();
1035       motionInfo.angularAccel = SGVec3f::zeros();
1036     } else {
1037       // if the interface is suspendend, prevent the client from
1038       // wild extrapolations
1039       motionInfo.linearVel = SGVec3f::zeros();
1040       motionInfo.angularVel = SGVec3f::zeros();
1041       motionInfo.linearAccel = SGVec3f::zeros();
1042       motionInfo.angularAccel = SGVec3f::zeros();
1043     }
1044
1045     // now send the properties
1046     PropertyMap::iterator it;
1047     for (it = mPropertyMap.begin(); it != mPropertyMap.end(); ++it) {
1048       FGPropertyData* pData = new FGPropertyData;
1049       pData->id = it->first;
1050       pData->type = it->second->getType();
1051       
1052       switch (pData->type) {
1053         case props::INT:
1054         case props::LONG:
1055         case props::BOOL:
1056           pData->int_value = it->second->getIntValue();
1057           break;
1058         case props::FLOAT:
1059         case props::DOUBLE:
1060           pData->float_value = it->second->getFloatValue();
1061           break;
1062         case props::STRING:
1063         case props::UNSPECIFIED:
1064           {
1065             // FIXME: We assume unspecified are strings for the moment.
1066
1067             const char* cstr = it->second->getStringValue();
1068             int len = strlen(cstr);
1069             
1070             if (len > 0)
1071             {            
1072               pData->string_value = new char[len + 1];
1073               strcpy(pData->string_value, cstr);
1074             }
1075             else
1076             {
1077               // Size 0 - ignore
1078               pData->string_value = 0;            
1079             }
1080
1081             //cout << " Sending property " << pData->id << " " << pData->type << " " <<  pData->string_value << "\n";
1082             break;        
1083           }
1084         default:
1085           // FIXME Currently default to a float. 
1086           //cout << "Unknown type when iterating through props: " << pData->type << "\n";
1087           pData->float_value = it->second->getFloatValue();
1088           break;
1089       }
1090       
1091       motionInfo.properties.push_back(pData);
1092     }
1093
1094     SendMyPosition(motionInfo);
1095 }
1096
1097
1098 //////////////////////////////////////////////////////////////////////
1099 //
1100 //  handle a position message
1101 //
1102 //////////////////////////////////////////////////////////////////////
1103 void
1104 FGMultiplayMgr::ProcessPosMsg(const FGMultiplayMgr::MsgBuf& Msg,
1105                               const simgear::IPAddress& SenderAddress, long stamp)
1106 {
1107   const T_MsgHdr* MsgHdr = Msg.msgHdr();
1108   if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + sizeof(T_PositionMsg)) {
1109     SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - "
1110             << "Position message received with insufficient data" );
1111     return;
1112   }
1113   const T_PositionMsg* PosMsg = Msg.posMsg();
1114   FGExternalMotionData motionInfo;
1115   motionInfo.time = XDR_decode_double(PosMsg->time);
1116   motionInfo.lag = XDR_decode_double(PosMsg->lag);
1117   for (unsigned i = 0; i < 3; ++i)
1118     motionInfo.position(i) = XDR_decode_double(PosMsg->position[i]);
1119   SGVec3f angleAxis;
1120   for (unsigned i = 0; i < 3; ++i)
1121     angleAxis(i) = XDR_decode_float(PosMsg->orientation[i]);
1122   motionInfo.orientation = SGQuatf::fromAngleAxis(angleAxis);
1123   for (unsigned i = 0; i < 3; ++i)
1124     motionInfo.linearVel(i) = XDR_decode_float(PosMsg->linearVel[i]);
1125   for (unsigned i = 0; i < 3; ++i)
1126     motionInfo.angularVel(i) = XDR_decode_float(PosMsg->angularVel[i]);
1127   for (unsigned i = 0; i < 3; ++i)
1128     motionInfo.linearAccel(i) = XDR_decode_float(PosMsg->linearAccel[i]);
1129   for (unsigned i = 0; i < 3; ++i)
1130     motionInfo.angularAccel(i) = XDR_decode_float(PosMsg->angularAccel[i]);
1131
1132   // sanity check: do not allow injection of corrupted data (NaNs)
1133   if (!isSane(motionInfo))
1134   {
1135       // drop this message, keep old position until receiving valid data
1136       SG_LOG(SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::ProcessPosMsg - "
1137               << "Position message with invalid data (NaN) received from "
1138               << MsgHdr->Callsign);
1139       return;
1140   }
1141
1142   //cout << "INPUT MESSAGE\n";
1143
1144   // There was a bug in 1.9.0 and before: T_PositionMsg was 196 bytes
1145   // on 32 bit architectures and 200 bytes on 64 bit, and this
1146   // structure is put directly on the wire. By looking at the padding,
1147   // we can sort through the mess, mostly:
1148   // If padding is 0 (which is not a valid property type), then the
1149   // message was produced by a new client or an old 64 bit client that
1150   // happened to have 0 on the stack;
1151   // Else if the property list starting with the padding word is
1152   // well-formed, then the client is probably an old 32 bit client and
1153   // we'll go with that;
1154   // Else it is an old 64-bit client and properties start after the
1155   // padding.
1156   // There is a chance that we could be fooled by garbage in the
1157   // padding looking like a valid property, so verifyProperties() is
1158   // strict about the validity of the property values.
1159   const xdr_data_t* xdr = Msg.properties();
1160   if (PosMsg->pad != 0) {
1161     if (verifyProperties(&PosMsg->pad, Msg.propsRecvdEnd()))
1162       xdr = &PosMsg->pad;
1163     else if (!verifyProperties(xdr, Msg.propsRecvdEnd()))
1164       goto noprops;
1165   }
1166   while (xdr < Msg.propsRecvdEnd()) {
1167     // simgear::props::Type type = simgear::props::UNSPECIFIED;
1168     
1169     // First element is always the ID
1170     unsigned id = XDR_decode_uint32(*xdr);
1171     //cout << pData->id << " ";
1172     xdr++;
1173     
1174     // Check the ID actually exists and get the type
1175     const IdPropertyList* plist = findProperty(id);
1176     
1177     if (plist)
1178     {
1179       FGPropertyData* pData = new FGPropertyData;
1180       pData->id = id;
1181       pData->type = plist->type;
1182       // How we decode the remainder of the property depends on the type
1183       switch (pData->type) {
1184         case simgear::props::INT:
1185         case simgear::props::BOOL:
1186         case simgear::props::LONG:
1187           pData->int_value = XDR_decode_uint32(*xdr);
1188           xdr++;
1189           //cout << pData->int_value << "\n";
1190           break;
1191         case simgear::props::FLOAT:
1192         case simgear::props::DOUBLE:
1193           pData->float_value = XDR_decode_float(*xdr);
1194           xdr++;
1195           //cout << pData->float_value << "\n";
1196           break;
1197         case simgear::props::STRING:
1198         case simgear::props::UNSPECIFIED:
1199           {
1200             // String is complicated. It consists of
1201             // The length of the string
1202             // The string itself
1203             // Padding to the nearest 4-bytes.    
1204             uint32_t length = XDR_decode_uint32(*xdr);
1205             xdr++;
1206             //cout << length << " ";
1207             // Old versions truncated the string but left the length unadjusted.
1208             if (length > MAX_TEXT_SIZE)
1209               length = MAX_TEXT_SIZE;
1210             pData->string_value = new char[length + 1];
1211             //cout << " String: ";
1212             for (unsigned i = 0; i < length; i++)
1213               {
1214                 pData->string_value[i] = (char) XDR_decode_int8(*xdr);
1215                 xdr++;
1216                 //cout << pData->string_value[i];
1217               }
1218
1219             pData->string_value[length] = '\0';
1220
1221             // Now handle the padding
1222             while ((length % 4) != 0)
1223               {
1224                 xdr++;
1225                 length++;
1226                 //cout << "0";
1227               }
1228             //cout << "\n";
1229           }
1230           break;
1231
1232         default:
1233           pData->float_value = XDR_decode_float(*xdr);
1234           SG_LOG(SG_NETWORK, SG_DEBUG, "Unknown Prop type " << pData->id << " " << pData->type);
1235           xdr++;
1236           break;
1237       }
1238
1239       motionInfo.properties.push_back(pData);
1240     }
1241     else
1242     {
1243       // We failed to find the property. We'll try the next packet immediately.
1244       SG_LOG(SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::ProcessPosMsg - "
1245              "message from " << MsgHdr->Callsign << " has unknown property id "
1246              << id); 
1247     }
1248   }
1249  noprops:
1250   FGAIMultiplayer* mp = getMultiplayer(MsgHdr->Callsign);
1251   if (!mp)
1252     mp = addMultiplayer(MsgHdr->Callsign, PosMsg->Model);
1253   mp->addMotionInfo(motionInfo, stamp);
1254 } // FGMultiplayMgr::ProcessPosMsg()
1255 //////////////////////////////////////////////////////////////////////
1256
1257 //////////////////////////////////////////////////////////////////////
1258 //
1259 //  handle a chat message
1260 //  FIXME: display chat message within flightgear
1261 //
1262 //////////////////////////////////////////////////////////////////////
1263 void
1264 FGMultiplayMgr::ProcessChatMsg(const MsgBuf& Msg,
1265                                const simgear::IPAddress& SenderAddress)
1266 {
1267   const T_MsgHdr* MsgHdr = Msg.msgHdr();
1268   if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + 1) {
1269     SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - "
1270             << "Chat message received with insufficient data" );
1271     return;
1272   }
1273   
1274   char *chatStr = new char[MsgHdr->MsgLen - sizeof(T_MsgHdr)];
1275   const T_ChatMsg* ChatMsg
1276       = reinterpret_cast<const T_ChatMsg *>(Msg.Msg + sizeof(T_MsgHdr));
1277   strncpy(chatStr, ChatMsg->Text,
1278           MsgHdr->MsgLen - sizeof(T_MsgHdr));
1279   chatStr[MsgHdr->MsgLen - sizeof(T_MsgHdr) - 1] = '\0';
1280   
1281   SG_LOG (SG_NETWORK, SG_WARN, "Chat [" << MsgHdr->Callsign << "]"
1282            << " " << chatStr);
1283
1284   delete [] chatStr;
1285 } // FGMultiplayMgr::ProcessChatMsg ()
1286 //////////////////////////////////////////////////////////////////////
1287
1288 void
1289 FGMultiplayMgr::FillMsgHdr(T_MsgHdr *MsgHdr, int MsgId, unsigned _len)
1290 {
1291   uint32_t len;
1292   switch (MsgId) {
1293   case CHAT_MSG_ID:
1294     len = sizeof(T_MsgHdr) + sizeof(T_ChatMsg);
1295     break;
1296   case POS_DATA_ID:
1297     len = _len;
1298     break;
1299   default:
1300     len = sizeof(T_MsgHdr);
1301     break;
1302   }
1303   MsgHdr->Magic           = XDR_encode_uint32(MSG_MAGIC);
1304   MsgHdr->Version         = XDR_encode_uint32(PROTO_VER);
1305   MsgHdr->MsgId           = XDR_encode_uint32(MsgId);
1306   MsgHdr->MsgLen          = XDR_encode_uint32(len);
1307   MsgHdr->ReplyAddress    = 0; // Are obsolete, keep them for the server for
1308   MsgHdr->ReplyPort       = 0; // now
1309   strncpy(MsgHdr->Callsign, mCallsign.c_str(), MAX_CALLSIGN_LEN);
1310   MsgHdr->Callsign[MAX_CALLSIGN_LEN - 1] = '\0';
1311 }
1312
1313 FGAIMultiplayer*
1314 FGMultiplayMgr::addMultiplayer(const std::string& callsign,
1315                                const std::string& modelName)
1316 {
1317   if (0 < mMultiPlayerMap.count(callsign))
1318     return mMultiPlayerMap[callsign].get();
1319
1320   FGAIMultiplayer* mp = new FGAIMultiplayer;
1321   mp->setPath(modelName.c_str());
1322   mp->setCallSign(callsign);
1323   mMultiPlayerMap[callsign] = mp;
1324
1325   FGAIManager *aiMgr = (FGAIManager*)globals->get_subsystem("ai-model");
1326   if (aiMgr) {
1327     aiMgr->attach(mp);
1328
1329     /// FIXME: that must follow the attach ATM ...
1330     for (unsigned i = 0; i < numProperties; ++i)
1331       mp->addPropertyId(sIdPropertyList[i].id, sIdPropertyList[i].name);
1332   }
1333
1334   return mp;
1335 }
1336
1337 FGAIMultiplayer*
1338 FGMultiplayMgr::getMultiplayer(const std::string& callsign)
1339 {
1340   if (0 < mMultiPlayerMap.count(callsign))
1341     return mMultiPlayerMap[callsign].get();
1342   else
1343     return 0;
1344 }
1345
1346 void
1347 FGMultiplayMgr::findProperties()
1348 {
1349   if (!mPropertiesChanged) {
1350     return;
1351   }
1352   
1353   mPropertiesChanged = false;
1354   
1355   for (unsigned i = 0; i < numProperties; ++i) {
1356       const char* name = sIdPropertyList[i].name;
1357       SGPropertyNode* pNode = globals->get_props()->getNode(name);
1358       if (!pNode) {
1359         continue;
1360       }
1361       
1362       int id = sIdPropertyList[i].id;
1363       if (mPropertyMap.find(id) != mPropertyMap.end()) {
1364         continue; // already activated
1365       }
1366       
1367       mPropertyMap[id] = pNode;
1368       SG_LOG(SG_NETWORK, SG_DEBUG, "activating MP property:" << pNode->getPath());
1369     }
1370 }