]> git.mxchange.org Git - flightgear.git/blob - src/MultiPlayer/multiplaymgr.cxx
Vivian Meazza:
[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 // With minor additions by Vivian Meazza, January 2006
9 //
10 // Copyright (C) 2003  Airservices Australia
11 // Copyright (C) 2005  Oliver Schroeder
12 //
13 // This program is free software; you can redistribute it and/or
14 // modify it under the terms of the GNU General Public License as
15 // published by the Free Software Foundation; either version 2 of the
16 // License, or (at your option) any later version.
17 //
18 // This program is distributed in the hope that it will be useful, but
19 // WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21 // General Public License for more details.
22 //
23 // You should have received a copy of the GNU General Public License
24 // along with this program; if not, write to the Free Software
25 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 //
27 // $Id$
28 //  
29 //////////////////////////////////////////////////////////////////////
30
31 #ifdef HAVE_CONFIG_H
32 #include <config.h>
33 #endif
34
35 #ifdef FG_MPLAYER_AS
36
37 #include <sys/types.h>
38 #if !(defined(_MSC_VER) || defined(__MINGW32__))
39 #   include <sys/socket.h>
40 #   include <netinet/in.h>
41 #   include <arpa/inet.h>
42 #endif
43 #include <stdlib.h>
44
45 #include <plib/netSocket.h>
46
47 #include <simgear/debug/logstream.hxx>
48
49 #include <Main/fg_props.hxx>
50 #include "multiplaymgr.hxx"
51 #include "mpmessages.hxx"
52 #include "mpplayer.hxx"
53
54 #define MAX_PACKET_SIZE 1024
55
56 // These constants are provided so that the ident 
57 // command can list file versions
58 const char sMULTIPLAYMGR_BID[] = 
59     "$Id$";
60 const char sMULTIPLAYMGR_HID[] = MULTIPLAYTXMGR_HID;
61
62 //////////////////////////////////////////////////////////////////////
63 //
64 //  MultiplayMgr constructor
65 //
66 //////////////////////////////////////////////////////////////////////
67 FGMultiplayMgr::FGMultiplayMgr() 
68 {
69     m_Initialised   = false;
70     m_LocalPlayer   = NULL;
71     m_RxAddress     = "0";
72     m_RxPort        = 0;
73     m_Initialised   = false;
74     m_HaveServer    = false;
75
76         send_all_props= false;
77 } // FGMultiplayMgr::FGMultiplayMgr()
78 //////////////////////////////////////////////////////////////////////
79
80 //////////////////////////////////////////////////////////////////////
81 //
82 //  MultiplayMgr destructor
83 //
84 //////////////////////////////////////////////////////////////////////
85 FGMultiplayMgr::~FGMultiplayMgr() 
86 {
87     Close();
88 } // FGMultiplayMgr::~FGMultiplayMgr()
89 //////////////////////////////////////////////////////////////////////
90
91 //////////////////////////////////////////////////////////////////////
92 // Name: getSendAll
93 // Description: getter for send_all
94 /////////////////////////////////////////////////////////////////////
95 bool FGMultiplayMgr::getSendAllProps() {
96         return send_all_props;
97 }
98
99 //////////////////////////////////////////////////////////////////////
100 // Name: setSendAll
101 // Description: setter for send_all
102 /////////////////////////////////////////////////////////////////////
103 void FGMultiplayMgr::setSendAllProps(bool s) {
104         send_all_props = s;
105 }
106
107 //////////////////////////////////////////////////////////////////////
108 //
109 //  Initialise object
110 //
111 //////////////////////////////////////////////////////////////////////
112 bool
113 FGMultiplayMgr::init (void) 
114 {
115     string  TxAddress;      // Destination address
116     int     TxPort;
117
118     //////////////////////////////////////////////////
119     //  Initialise object if not already done
120     //////////////////////////////////////////////////
121     if (m_Initialised) 
122     {
123         SG_LOG( SG_NETWORK, SG_WARN,
124           "FGMultiplayMgr::init - already initialised" );
125         return (false);
126     }
127     //////////////////////////////////////////////////
128     //  Set members from property values
129     //////////////////////////////////////////////////
130     TxAddress      = fgGetString   ("/sim/multiplay/txhost");
131     TxPort         = fgGetInt      ("/sim/multiplay/txport");
132     m_Callsign     = fgGetString   ("/sim/multiplay/callsign");
133     m_RxAddress    = fgGetString   ("/sim/multiplay/rxhost");
134     m_RxPort       = fgGetInt      ("/sim/multiplay/rxport");
135     if (m_RxPort <= 0)
136     {
137         m_RxPort = 5000;
138     }
139     if (m_Callsign == "")
140     {
141         // FIXME: use getpwuid
142         m_Callsign = "JohnDoe"; 
143     }
144     if (m_RxAddress == "")
145     {
146         m_RxAddress = "127.0.0.1";
147     }
148     if ((TxPort > 0) && (TxAddress != ""))
149     {
150         m_HaveServer = true;
151         m_Server.set (TxAddress.c_str(), TxPort); 
152     }
153     SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-txaddress= "<<TxAddress);
154     SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-txport= "<<TxPort );
155     SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-rxaddress="<<m_RxAddress );
156     SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-rxport= "<<m_RxPort);
157     SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-callsign= "<<m_Callsign);
158     m_DataSocket = new netSocket();
159     if (!m_DataSocket->open(false))
160     {
161         SG_LOG( SG_NETWORK, SG_ALERT,
162           "FGMultiplayMgr::init - Failed to create data socket" );
163         return (false);
164     }
165     m_DataSocket->setBlocking(false);
166     m_DataSocket->setBroadcast(true);
167     if (m_DataSocket->bind(m_RxAddress.c_str(), m_RxPort) != 0)
168     {
169         perror("bind");
170         SG_LOG( SG_NETWORK, SG_ALERT,
171           "FGMultiplayMgr::Open - Failed to bind receive socket" );
172         return (false);
173     }
174     m_LocalPlayer = new MPPlayer();
175     if (!m_LocalPlayer->Open(m_RxAddress, m_RxPort, m_Callsign,
176                             fgGetString("/sim/model/path"), true)) 
177     {
178         SG_LOG( SG_NETWORK, SG_ALERT,
179           "FGMultiplayMgr::init - Failed to create local player" );
180         return (false);
181     }
182     m_Initialised = true;
183         send_all_props= true;
184     return (true);
185 } // FGMultiplayMgr::init()
186 //////////////////////////////////////////////////////////////////////
187
188 //////////////////////////////////////////////////////////////////////
189 //
190 //  Closes and deletes the local player object. Closes
191 //  and deletes the tx socket. Resets the object state to unitialised.
192 //
193 //////////////////////////////////////////////////////////////////////
194 void
195 FGMultiplayMgr::Close (void) 
196 {
197     //////////////////////////////////////////////////
198     //  Delete local player
199     //////////////////////////////////////////////////
200     if (m_LocalPlayer) 
201     {
202         delete m_LocalPlayer;
203         m_LocalPlayer = NULL;
204     }
205     //////////////////////////////////////////////////
206     //  Delete any existing players
207     //////////////////////////////////////////////////
208     t_MPClientListIterator CurrentPlayer;
209     t_MPClientListIterator P;
210     CurrentPlayer  = m_MPClientList.begin ();
211     while (CurrentPlayer != m_MPClientList.end ())
212     {
213         P = CurrentPlayer;
214         delete (*P);
215         *P = 0;
216         CurrentPlayer = m_MPClientList.erase (P);
217     }
218     //////////////////////////////////////////////////
219     //  Delete socket
220     //////////////////////////////////////////////////
221     if (m_DataSocket) 
222     {
223         m_DataSocket->close();
224         delete m_DataSocket;
225         m_DataSocket = NULL;
226     }
227     m_Initialised = false;
228 } // FGMultiplayMgr::Close(void)
229 //////////////////////////////////////////////////////////////////////
230
231 //////////////////////////////////////////////////////////////////////
232 //
233 //  Description: Sends the position data for the local position.
234 //
235 //////////////////////////////////////////////////////////////////////
236 void
237 FGMultiplayMgr::SendMyPosition
238     (
239         const double lat, const double lon, const double alt,
240         const double heading, const double roll, const double pitch,
241         const double speedN, const double speedE, const double speedD,
242         const double left_aileron, const double right_aileron, const double elevator, const double rudder,
243         //const double rpms[6],
244         const double rateH, const double rateR, const double rateP,
245                 const double accN, const double accE, const double accD
246     )
247 {
248     T_MsgHdr        MsgHdr;
249     T_PositionMsg   PosMsg;
250     char Msg[sizeof(T_MsgHdr) + sizeof(T_PositionMsg)];
251
252     if ((! m_Initialised) || (! m_HaveServer))
253     {
254         if (! m_Initialised)
255         SG_LOG( SG_NETWORK, SG_ALERT,
256           "FGMultiplayMgr::SendMyPosition - not initialised" );
257         if (! m_HaveServer)
258         SG_LOG( SG_NETWORK, SG_ALERT,
259           "FGMultiplayMgr::SendMyPosition - no server" );
260         return;
261     }
262     m_LocalPlayer->SetPosition(lat, lon, alt,
263                                heading, roll, pitch,
264                                speedN, speedE, speedD,
265                                left_aileron, right_aileron, elevator, rudder,
266                                //rpms,
267                                rateH, rateR, rateP,
268                                                            accN, accE, accD);
269     m_LocalPlayer->FillPosMsg(&MsgHdr, &PosMsg);
270     memcpy(Msg, &MsgHdr, sizeof(T_MsgHdr));
271     memcpy(Msg + sizeof(T_MsgHdr), &PosMsg, sizeof(T_PositionMsg));
272     m_DataSocket->sendto (Msg,
273       sizeof(T_MsgHdr) + sizeof(T_PositionMsg), 0, &m_Server);
274         SG_LOG( SG_NETWORK, SG_DEBUG,
275           "FGMultiplayMgr::SendMyPosition" );
276         
277 } // FGMultiplayMgr::SendMyPosition()
278 //////////////////////////////////////////////////////////////////////
279
280 //////////////////////////////////////////////////////////////////////
281 //
282 //  Description: Sends the property data for the local player.
283 //
284 //////////////////////////////////////////////////////////////////////
285 void FGMultiplayMgr::SendPropMessage (const string &property, SGPropertyNode::Type type, double value)
286         
287         {SG_LOG( SG_NETWORK, SG_INFO,
288           "FGMultiplayMgr::Property: " << property << " Type " << type << " value " << value);
289
290     T_MsgHdr      MsgHdr;
291     T_PropertyMsg PropMsg;
292     unsigned int iNextBlockPosition = 0;
293     char Msg[sizeof(T_MsgHdr) + sizeof(T_PropertyMsg)];
294
295     if ((! m_Initialised) || (! m_HaveServer))
296     {
297         return;
298     }
299     m_LocalPlayer->FillMsgHdr(&MsgHdr, PROP_MSG_ID);
300     
301     strncpy(PropMsg.property, property.c_str(), MAX_PROPERTY_LEN);
302         PropMsg.property[MAX_PROPERTY_LEN-1] = '\0';
303         PropMsg.type = XDR_encode_uint32(type);
304     PropMsg.val = XDR_encode_double(value);
305     SG_LOG( SG_NETWORK, SG_INFO,
306                 "FGMultiplayMgr::sending property message: "
307                  << PropMsg.property << " " << PropMsg.type << " " << PropMsg.val);
308     
309         memcpy (Msg, &MsgHdr, sizeof(T_MsgHdr));
310     memcpy (Msg + sizeof(T_MsgHdr), &PropMsg, sizeof(T_PropertyMsg));
311     m_DataSocket->sendto (Msg,
312                           sizeof(T_MsgHdr) + sizeof(T_PropertyMsg), 0, &m_Server);
313 } // FGMultiplayMgr::SendPropMessage ()
314
315 //////////////////////////////////////////////////////////////////////
316 //
317 //  Name: SendTextMessage
318 //  Description: Sends a message to the player. The message must
319 //  contain a valid and correctly filled out header and optional
320 //  message body.
321 //
322 //////////////////////////////////////////////////////////////////////
323 void
324 FGMultiplayMgr::SendTextMessage
325     (
326     const string &MsgText
327     ) const
328 {
329     T_MsgHdr    MsgHdr;
330     T_ChatMsg   ChatMsg;
331     unsigned int iNextBlockPosition = 0;
332     char Msg[sizeof(T_MsgHdr) + sizeof(T_ChatMsg)];
333
334     if ((! m_Initialised) || (! m_HaveServer))
335     {
336         return;
337     }
338     m_LocalPlayer->FillMsgHdr(&MsgHdr, CHAT_MSG_ID);
339     //////////////////////////////////////////////////
340     // Divide the text string into blocks that fit
341     // in the message and send the blocks.
342     //////////////////////////////////////////////////
343     while (iNextBlockPosition < MsgText.length()) 
344     {
345         strncpy (ChatMsg.Text, 
346           MsgText.substr(iNextBlockPosition, MAX_CHAT_MSG_LEN - 1).c_str(),
347           MAX_CHAT_MSG_LEN);
348         ChatMsg.Text[MAX_CHAT_MSG_LEN - 1] = '\0';
349         memcpy (Msg, &MsgHdr, sizeof(T_MsgHdr));
350         memcpy (Msg + sizeof(T_MsgHdr), &ChatMsg, sizeof(T_ChatMsg));
351         m_DataSocket->sendto (Msg,
352           sizeof(T_MsgHdr) + sizeof(T_ChatMsg), 0, &m_Server);
353         iNextBlockPosition += MAX_CHAT_MSG_LEN - 1;
354     }
355 } // FGMultiplayMgr::SendTextMessage ()
356 //////////////////////////////////////////////////////////////////////
357
358 //////////////////////////////////////////////////////////////////////
359 //
360 //  Name: ProcessData
361 //  Description: Processes data waiting at the receive socket. The
362 //  processing ends when there is no more data at the socket.
363 //  
364 //////////////////////////////////////////////////////////////////////
365 void
366 FGMultiplayMgr::ProcessData (void) 
367 {
368     char        Msg[MAX_PACKET_SIZE];   // Buffer for received message
369     int         Bytes;                  // Bytes received
370     T_MsgHdr*   MsgHdr;                 // Pointer to header in received data
371     netAddress  SenderAddress;
372
373     if (! m_Initialised)
374     {
375         SG_LOG( SG_NETWORK, SG_ALERT,
376           "FGMultiplayMgr::ProcessData - not initialised" );
377         return;
378     }
379     //////////////////////////////////////////////////
380     //  Read the receive socket and process any data
381     //////////////////////////////////////////////////
382     do {
383         //////////////////////////////////////////////////
384         //  Although the recv call asks for 
385         //  MAX_PACKET_SIZE of data, the number of bytes
386         //  returned will only be that of the next
387         //  packet waiting to be processed.
388         //////////////////////////////////////////////////
389         Bytes = m_DataSocket->recvfrom (Msg, MAX_PACKET_SIZE, 0,
390                                          &SenderAddress);
391         //////////////////////////////////////////////////
392         //  no Data received
393         //////////////////////////////////////////////////
394         if (Bytes <= 0)
395         {
396             if (errno != EAGAIN)
397             {
398                 perror("FGMultiplayMgr::MP_ProcessData");
399             }
400             return;
401         }
402         if (Bytes <= (int)sizeof(MsgHdr)) 
403         {
404             SG_LOG( SG_NETWORK, SG_ALERT,
405               "FGMultiplayMgr::MP_ProcessData - "
406               << "received message with insufficient data" );
407             return;
408         }
409         //////////////////////////////////////////////////
410         //  Read header
411         //////////////////////////////////////////////////
412         MsgHdr = (T_MsgHdr *)Msg;
413         MsgHdr->Magic       = XDR_decode_uint32 (MsgHdr->Magic);
414         MsgHdr->Version     = XDR_decode_uint32 (MsgHdr->Version);
415         MsgHdr->MsgId       = XDR_decode_uint32 (MsgHdr->MsgId);
416         MsgHdr->MsgLen      = XDR_decode_uint32 (MsgHdr->MsgLen);
417         MsgHdr->ReplyPort   = XDR_decode_uint32 (MsgHdr->ReplyPort);
418         if (MsgHdr->Magic != MSG_MAGIC)
419         {
420             SG_LOG( SG_NETWORK, SG_ALERT,
421               "FGMultiplayMgr::MP_ProcessData - "
422               << "message has invalid magic number!" );
423         }
424         if (MsgHdr->Version != PROTO_VER) 
425         {
426             SG_LOG( SG_NETWORK, SG_ALERT,
427               "FGMultiplayMgr::MP_ProcessData - "
428               << "message has invalid protocoll number!" );
429         }
430         //////////////////////////////////////////////////
431         //  Process the player data unless we generated it
432         //////////////////////////////////////////////////
433         if (m_Callsign == MsgHdr->Callsign) 
434         {
435             return;
436         }
437         //////////////////////////////////////////////////
438         //  Process messages
439         //////////////////////////////////////////////////
440         switch(MsgHdr->MsgId)
441         {
442             case CHAT_MSG_ID:
443                 ProcessChatMsg ((char*) & Msg, SenderAddress);
444                 break;
445             case POS_DATA_ID:
446                 ProcessPosMsg ((char*) & Msg, SenderAddress);
447                 break;
448             case PROP_MSG_ID:
449                 ProcessPropMsg ((char *) & Msg, SenderAddress);
450                 break;
451             default:
452                 SG_LOG( SG_NETWORK, SG_ALERT,
453                   "FGMultiplayMgr::MP_ProcessData - "
454                   << "Unknown message Id received: " 
455                   << MsgHdr->MsgId );
456                 break;
457         } // switch
458     } while (Bytes > 0);
459 } // FGMultiplayMgr::ProcessData(void)
460 //////////////////////////////////////////////////////////////////////
461
462 //////////////////////////////////////////////////////////////////////
463 //
464 //  handle a position message
465 //
466 //////////////////////////////////////////////////////////////////////
467 void
468 FGMultiplayMgr::ProcessPosMsg
469     (
470     const char *Msg,
471     netAddress & SenderAddress
472     )
473 {
474     T_PositionMsg*  PosMsg;     // Pointer to position message in received data
475     T_MsgHdr*       MsgHdr;     // Pointer to header in received data
476     bool            ActivePlayer; 
477     struct in_addr  PlayerAddress;
478     int             time, timeusec;
479     double          lat, lon, alt;
480     double          hdg, roll, pitch;
481     double          speedN, speedE, speedD;
482     double          left_aileron, right_aileron, elevator, rudder;
483     //double        rpms[6];
484     double          rateH, rateR, rateP;
485     double          accN, accE, accD;
486     t_MPClientListIterator CurrentPlayer;
487
488     ActivePlayer = false;
489     MsgHdr = (T_MsgHdr *)Msg;
490     if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + sizeof(T_PositionMsg))
491     {
492         SG_LOG( SG_NETWORK, SG_ALERT,
493           "FGMultiplayMgr::MP_ProcessData - "
494           << "Position message received with insufficient data" );
495         return;
496     } else if (MsgHdr->MsgLen > sizeof(T_MsgHdr) + sizeof(T_PositionMsg)) {
497         SG_LOG( SG_NETWORK, SG_ALERT,
498                 "FGMultiplayMgr::MP_ProcessData - "
499                 << "Position message received with more data than I can handle" );
500     }
501     PosMsg = (T_PositionMsg *)(Msg + sizeof(T_MsgHdr));
502     time = XDR_decode_uint32 (PosMsg->time);
503     timeusec = XDR_decode_uint32 (PosMsg->timeusec);
504     lat = XDR_decode_double (PosMsg->lat);
505     lon = XDR_decode_double (PosMsg->lon);
506     alt = XDR_decode_double (PosMsg->alt);
507     hdg = XDR_decode_double (PosMsg->hdg);
508     roll = XDR_decode_double (PosMsg->roll);
509     pitch = XDR_decode_double (PosMsg->pitch);
510     speedN = XDR_decode_double (PosMsg->speedN);
511     speedE = XDR_decode_double (PosMsg->speedE);
512     speedD = XDR_decode_double (PosMsg->speedD);
513     left_aileron = XDR_decode_float (PosMsg->left_aileron);
514     right_aileron = XDR_decode_float (PosMsg->right_aileron);
515     elevator = XDR_decode_float (PosMsg->elevator);
516     rudder = XDR_decode_float (PosMsg->rudder);
517     /*for (int i = 0; i < 6; i++) {
518         rpms[i] = XDR_decode_float (PosMsg->rpms[i]);
519     }*/
520     rateH = XDR_decode_float (PosMsg->rateH);
521     rateR = XDR_decode_float (PosMsg->rateR);
522     rateP = XDR_decode_float (PosMsg->rateP);
523     accN = XDR_decode_float (PosMsg->accN);
524     accE = XDR_decode_float (PosMsg->accE);
525     accD = XDR_decode_float (PosMsg->accD);
526
527     //////////////////////////////////////////////////
528     //  Check if the player is already in the game 
529     //  by using the Callsign
530     //////////////////////////////////////////////////
531     for (CurrentPlayer  = m_MPClientList.begin ();
532          CurrentPlayer != m_MPClientList.end ();
533          CurrentPlayer++)
534     {
535         if ((*CurrentPlayer)->CompareCallsign(MsgHdr->Callsign))
536         {
537             // Player found. Update the data for the player if the timestamp is OK
538             if ((*CurrentPlayer)->CheckTime(time, timeusec))
539                 (*CurrentPlayer)->SetPosition(lat, lon, alt,
540                                               hdg, roll, pitch,
541                                               speedN, speedE, speedD,
542                                               left_aileron, right_aileron, elevator, rudder,
543                                               //rpms,
544                                               rateH, rateR, rateP,
545                                                                                           accN, accE, accD
546                                                                                           );
547             ActivePlayer = true;
548         }
549     } // for (...)
550     if (ActivePlayer) 
551     {   // nothing more to do
552         return;
553     }
554     //////////////////////////////////////////////////
555     //  Player not active, so add as new player
556     //////////////////////////////////////////////////
557     MPPlayer* NewPlayer;
558     NewPlayer = new MPPlayer;
559     NewPlayer->Open(SenderAddress.getHost(), MsgHdr->ReplyPort,
560         MsgHdr->Callsign, PosMsg->Model, false);
561     if (NewPlayer->CheckTime(time, timeusec))
562         NewPlayer->SetPosition(lat, lon, alt,
563                                hdg, roll, pitch,
564                                speedN, speedE, speedD,
565                                left_aileron, right_aileron, elevator, rudder,
566                                //rpms,
567                                rateH, rateR, rateP,
568                                                            accN, accE, accD);
569     m_MPClientList.push_back (NewPlayer);
570
571         // if we have a new player then we need to send all our properties
572         send_all_props = true;
573
574 } // FGMultiplayMgr::ProcessPosMsg()
575 //////////////////////////////////////////////////////////////////////
576
577 //////////////////////////////////////////////////////////////////////
578 //
579 //  handle a chat message
580 //  FIXME: display chat message withi flightgear
581 //
582 //////////////////////////////////////////////////////////////////////
583 void
584 FGMultiplayMgr::ProcessChatMsg
585     (
586     const char *Msg,
587     netAddress & SenderAddress
588     )
589 {
590     T_ChatMsg*  ChatMsg;    // Pointer to chat message in received data
591     T_MsgHdr*   MsgHdr;     // Pointer to header in received data
592
593     MsgHdr = (T_MsgHdr *)Msg;
594     if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + 1)
595     {
596         SG_LOG( SG_NETWORK, SG_ALERT,
597           "FGMultiplayMgr::MP_ProcessData - "
598           << "Chat message received with insufficient data" );
599         return;
600     }
601     
602     char *MsgBuf = new char[MsgHdr->MsgLen - sizeof(T_MsgHdr)];
603     strncpy(MsgBuf, ((T_ChatMsg *)(Msg + sizeof(T_MsgHdr)))->Text, MsgHdr->MsgLen - sizeof(T_MsgHdr));
604     MsgBuf[MsgHdr->MsgLen - sizeof(T_MsgHdr) - 1] = '\0';
605     
606     ChatMsg = (T_ChatMsg *)(Msg + sizeof(T_MsgHdr));
607     SG_LOG ( SG_NETWORK, SG_ALERT, 
608       "Chat [" << MsgHdr->Callsign << "]" << " " << MsgBuf << endl);
609     delete [] MsgBuf;
610 } // FGMultiplayMgr::ProcessChatMsg ()
611 //////////////////////////////////////////////////////////////////////
612
613 //////////////////////////////////////////////////////////////////////
614 //
615 //  handle a property message
616 //
617 //////////////////////////////////////////////////////////////////////
618 void FGMultiplayMgr::ProcessPropMsg ( const char *Msg, netAddress & SenderAddress )
619 {
620     T_PropertyMsg* PropMsg; // Pointer to the message itself
621     T_MsgHdr*      MsgHdr;  // Pointer to the message header
622     t_MPClientListIterator CurrentPlayer;
623     
624     MsgHdr = (T_MsgHdr *) Msg;
625        
626         if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + sizeof(T_PropertyMsg)) {
627         SG_LOG( SG_NETWORK, SG_ALERT,
628                 "FGMultiplayMgr::MP_ProcessData - "
629                 << "Properties message received with insufficient data" );
630                 return;
631         } else if (MsgHdr->MsgLen > sizeof(T_MsgHdr) + sizeof(T_PropertyMsg)) {
632         SG_LOG( SG_NETWORK, SG_ALERT,
633                 "FGMultiplayMgr::MP_ProcessData - "
634                 << "Properties message received with more data than I know how to handle" );
635                 return;
636         }
637     
638     PropMsg = (T_PropertyMsg *)(Msg + sizeof(T_MsgHdr));
639     
640     //////////////////////////////////////////////////
641     //  Check if the player is already in the game 
642     //  by using the Callsign, but don't activate
643     //  new players (they need to send a position
644     //  message)
645     //////////////////////////////////////////////////
646     for (CurrentPlayer  = m_MPClientList.begin ();
647          CurrentPlayer != m_MPClientList.end ();
648          CurrentPlayer++)
649     {
650         if ((*CurrentPlayer)->CompareCallsign(MsgHdr->Callsign))
651         {
652             // Player found. Update the data for the player.
653             (*CurrentPlayer)->SetProperty(PropMsg->property,
654                                           (SGPropertyNode::Type) XDR_decode_uint32(PropMsg->type),
655                                           XDR_decode_double(PropMsg->val));
656                         SG_LOG( SG_NETWORK, SG_INFO,
657                 "FGMultiplayMgr::MP_ProcessData - "
658                 << PropMsg->property
659                 << (SGPropertyNode::Type) XDR_decode_uint32(PropMsg->type)
660                                 << XDR_decode_double(PropMsg->val) );
661         }
662         } // for (...)
663 }
664
665 //////////////////////////////////////////////////////////////////////
666 //
667 //  For each active player, tell the player object
668 //  to update its position on the scene. If a player object
669 //  returns status information indicating that it has not
670 //  had an update for some time then the player is deleted.
671 //
672 //////////////////////////////////////////////////////////////////////
673 void
674 FGMultiplayMgr::Update (void) 
675 {
676     MPPlayer::TPlayerDataState ePlayerDataState;
677     t_MPClientListIterator CurrentPlayer;
678
679     CurrentPlayer  = m_MPClientList.begin ();
680     while (CurrentPlayer != m_MPClientList.end ())
681     {
682         ePlayerDataState = (*CurrentPlayer)->Draw();
683         //////////////////////////////////////////////////
684         // If the player has not received an update for
685         // some time then assume that the player has quit.
686         //////////////////////////////////////////////////
687         if (ePlayerDataState == MPPlayer::PLAYER_DATA_EXPIRED) 
688         {
689             SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::Update - "
690               << "Deleting player from game. Callsign: "
691               << (*CurrentPlayer)->Callsign() );
692             t_MPClientListIterator P;
693                 P = CurrentPlayer;
694                 delete (*P);
695             *P = 0;
696             CurrentPlayer = m_MPClientList.erase (P);
697         }
698         else    CurrentPlayer++;
699     }
700 } // FGMultiplayMgr::Update()
701 //////////////////////////////////////////////////////////////////////
702
703 #endif // FG_MPLAYER_AS
704