1 //////////////////////////////////////////////////////////////////////
5 // Written by Duncan McCreanor, started February 2003.
6 // duncan.mccreanor@airservicesaustralia.com
8 // With minor additions by Vivian Meazza, January 2006
10 // Copyright (C) 2003 Airservices Australia
11 // Copyright (C) 2005 Oliver Schroeder
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.
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.
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.
29 //////////////////////////////////////////////////////////////////////
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>
45 #include <plib/netSocket.h>
47 #include <simgear/debug/logstream.hxx>
49 #include <Main/fg_props.hxx>
50 #include "multiplaymgr.hxx"
51 #include "mpmessages.hxx"
52 #include "mpplayer.hxx"
54 #define MAX_PACKET_SIZE 1024
56 // These constants are provided so that the ident
57 // command can list file versions
58 const char sMULTIPLAYMGR_BID[] =
60 const char sMULTIPLAYMGR_HID[] = MULTIPLAYTXMGR_HID;
62 //////////////////////////////////////////////////////////////////////
64 // MultiplayMgr constructor
66 //////////////////////////////////////////////////////////////////////
67 FGMultiplayMgr::FGMultiplayMgr()
69 m_Initialised = false;
73 m_Initialised = false;
76 send_all_props= false;
77 } // FGMultiplayMgr::FGMultiplayMgr()
78 //////////////////////////////////////////////////////////////////////
80 //////////////////////////////////////////////////////////////////////
82 // MultiplayMgr destructor
84 //////////////////////////////////////////////////////////////////////
85 FGMultiplayMgr::~FGMultiplayMgr()
88 } // FGMultiplayMgr::~FGMultiplayMgr()
89 //////////////////////////////////////////////////////////////////////
91 //////////////////////////////////////////////////////////////////////
93 // Description: getter for send_all
94 /////////////////////////////////////////////////////////////////////
95 bool FGMultiplayMgr::getSendAllProps() {
96 return send_all_props;
99 //////////////////////////////////////////////////////////////////////
101 // Description: setter for send_all
102 /////////////////////////////////////////////////////////////////////
103 void FGMultiplayMgr::setSendAllProps(bool s) {
107 //////////////////////////////////////////////////////////////////////
111 //////////////////////////////////////////////////////////////////////
113 FGMultiplayMgr::init (void)
115 string TxAddress; // Destination address
118 //////////////////////////////////////////////////
119 // Initialise object if not already done
120 //////////////////////////////////////////////////
123 SG_LOG( SG_NETWORK, SG_WARN,
124 "FGMultiplayMgr::init - already initialised" );
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");
139 if (m_Callsign == "")
141 // FIXME: use getpwuid
142 m_Callsign = "JohnDoe";
144 if (m_RxAddress == "")
146 m_RxAddress = "127.0.0.1";
148 if ((TxPort > 0) && (TxAddress != ""))
151 m_Server.set (TxAddress.c_str(), TxPort);
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))
161 SG_LOG( SG_NETWORK, SG_ALERT,
162 "FGMultiplayMgr::init - Failed to create data socket" );
165 m_DataSocket->setBlocking(false);
166 m_DataSocket->setBroadcast(true);
167 if (m_DataSocket->bind(m_RxAddress.c_str(), m_RxPort) != 0)
170 SG_LOG( SG_NETWORK, SG_ALERT,
171 "FGMultiplayMgr::Open - Failed to bind receive socket" );
174 m_LocalPlayer = new MPPlayer();
175 if (!m_LocalPlayer->Open(m_RxAddress, m_RxPort, m_Callsign,
176 fgGetString("/sim/model/path"), true))
178 SG_LOG( SG_NETWORK, SG_ALERT,
179 "FGMultiplayMgr::init - Failed to create local player" );
182 m_Initialised = true;
183 send_all_props= true;
185 } // FGMultiplayMgr::init()
186 //////////////////////////////////////////////////////////////////////
188 //////////////////////////////////////////////////////////////////////
190 // Closes and deletes the local player object. Closes
191 // and deletes the tx socket. Resets the object state to unitialised.
193 //////////////////////////////////////////////////////////////////////
195 FGMultiplayMgr::Close (void)
197 //////////////////////////////////////////////////
198 // Delete local player
199 //////////////////////////////////////////////////
202 delete m_LocalPlayer;
203 m_LocalPlayer = NULL;
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 ())
216 CurrentPlayer = m_MPClientList.erase (P);
218 //////////////////////////////////////////////////
220 //////////////////////////////////////////////////
223 m_DataSocket->close();
227 m_Initialised = false;
228 } // FGMultiplayMgr::Close(void)
229 //////////////////////////////////////////////////////////////////////
231 //////////////////////////////////////////////////////////////////////
233 // Description: Sends the position data for the local position.
235 //////////////////////////////////////////////////////////////////////
237 FGMultiplayMgr::SendMyPosition
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
249 T_PositionMsg PosMsg;
250 char Msg[sizeof(T_MsgHdr) + sizeof(T_PositionMsg)];
252 if ((! m_Initialised) || (! m_HaveServer))
255 SG_LOG( SG_NETWORK, SG_ALERT,
256 "FGMultiplayMgr::SendMyPosition - not initialised" );
258 SG_LOG( SG_NETWORK, SG_ALERT,
259 "FGMultiplayMgr::SendMyPosition - no server" );
262 m_LocalPlayer->SetPosition(lat, lon, alt,
263 heading, roll, pitch,
264 speedN, speedE, speedD,
265 left_aileron, right_aileron, elevator, rudder,
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" );
277 } // FGMultiplayMgr::SendMyPosition()
278 //////////////////////////////////////////////////////////////////////
280 //////////////////////////////////////////////////////////////////////
282 // Description: Sends the property data for the local player.
284 //////////////////////////////////////////////////////////////////////
285 void FGMultiplayMgr::SendPropMessage (const string &property, SGPropertyNode::Type type, double value)
287 {SG_LOG( SG_NETWORK, SG_INFO,
288 "FGMultiplayMgr::Property: " << property << " Type " << type << " value " << value);
291 T_PropertyMsg PropMsg;
292 unsigned int iNextBlockPosition = 0;
293 char Msg[sizeof(T_MsgHdr) + sizeof(T_PropertyMsg)];
295 if ((! m_Initialised) || (! m_HaveServer))
299 m_LocalPlayer->FillMsgHdr(&MsgHdr, PROP_MSG_ID);
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);
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 ()
315 //////////////////////////////////////////////////////////////////////
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
322 //////////////////////////////////////////////////////////////////////
324 FGMultiplayMgr::SendTextMessage
326 const string &MsgText
331 unsigned int iNextBlockPosition = 0;
332 char Msg[sizeof(T_MsgHdr) + sizeof(T_ChatMsg)];
334 if ((! m_Initialised) || (! m_HaveServer))
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())
345 strncpy (ChatMsg.Text,
346 MsgText.substr(iNextBlockPosition, MAX_CHAT_MSG_LEN - 1).c_str(),
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;
355 } // FGMultiplayMgr::SendTextMessage ()
356 //////////////////////////////////////////////////////////////////////
358 //////////////////////////////////////////////////////////////////////
361 // Description: Processes data waiting at the receive socket. The
362 // processing ends when there is no more data at the socket.
364 //////////////////////////////////////////////////////////////////////
366 FGMultiplayMgr::ProcessData (void)
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;
375 SG_LOG( SG_NETWORK, SG_ALERT,
376 "FGMultiplayMgr::ProcessData - not initialised" );
379 //////////////////////////////////////////////////
380 // Read the receive socket and process any data
381 //////////////////////////////////////////////////
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,
391 //////////////////////////////////////////////////
393 //////////////////////////////////////////////////
398 perror("FGMultiplayMgr::MP_ProcessData");
402 if (Bytes <= (int)sizeof(MsgHdr))
404 SG_LOG( SG_NETWORK, SG_ALERT,
405 "FGMultiplayMgr::MP_ProcessData - "
406 << "received message with insufficient data" );
409 //////////////////////////////////////////////////
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)
420 SG_LOG( SG_NETWORK, SG_ALERT,
421 "FGMultiplayMgr::MP_ProcessData - "
422 << "message has invalid magic number!" );
424 if (MsgHdr->Version != PROTO_VER)
426 SG_LOG( SG_NETWORK, SG_ALERT,
427 "FGMultiplayMgr::MP_ProcessData - "
428 << "message has invalid protocoll number!" );
430 //////////////////////////////////////////////////
431 // Process the player data unless we generated it
432 //////////////////////////////////////////////////
433 if (m_Callsign == MsgHdr->Callsign)
437 //////////////////////////////////////////////////
439 //////////////////////////////////////////////////
440 switch(MsgHdr->MsgId)
443 ProcessChatMsg ((char*) & Msg, SenderAddress);
446 ProcessPosMsg ((char*) & Msg, SenderAddress);
449 ProcessPropMsg ((char *) & Msg, SenderAddress);
452 SG_LOG( SG_NETWORK, SG_ALERT,
453 "FGMultiplayMgr::MP_ProcessData - "
454 << "Unknown message Id received: "
459 } // FGMultiplayMgr::ProcessData(void)
460 //////////////////////////////////////////////////////////////////////
462 //////////////////////////////////////////////////////////////////////
464 // handle a position message
466 //////////////////////////////////////////////////////////////////////
468 FGMultiplayMgr::ProcessPosMsg
471 netAddress & SenderAddress
474 T_PositionMsg* PosMsg; // Pointer to position message in received data
475 T_MsgHdr* MsgHdr; // Pointer to header in received data
478 double lat, lon, alt;
479 double hdg, roll, pitch;
480 double speedN, speedE, speedD;
481 double left_aileron, right_aileron, elevator, rudder;
483 double rateH, rateR, rateP;
484 double accN, accE, accD;
485 t_MPClientListIterator CurrentPlayer;
487 ActivePlayer = false;
488 MsgHdr = (T_MsgHdr *)Msg;
489 if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + sizeof(T_PositionMsg))
491 SG_LOG( SG_NETWORK, SG_ALERT,
492 "FGMultiplayMgr::MP_ProcessData - "
493 << "Position message received with insufficient data" );
495 } else if (MsgHdr->MsgLen > sizeof(T_MsgHdr) + sizeof(T_PositionMsg)) {
496 SG_LOG( SG_NETWORK, SG_ALERT,
497 "FGMultiplayMgr::MP_ProcessData - "
498 << "Position message received with more data than I can handle" );
500 PosMsg = (T_PositionMsg *)(Msg + sizeof(T_MsgHdr));
501 time = XDR_decode_uint32 (PosMsg->time);
502 timeusec = XDR_decode_uint32 (PosMsg->timeusec);
503 lat = XDR_decode_double (PosMsg->lat);
504 lon = XDR_decode_double (PosMsg->lon);
505 alt = XDR_decode_double (PosMsg->alt);
506 hdg = XDR_decode_double (PosMsg->hdg);
507 roll = XDR_decode_double (PosMsg->roll);
508 pitch = XDR_decode_double (PosMsg->pitch);
509 speedN = XDR_decode_double (PosMsg->speedN);
510 speedE = XDR_decode_double (PosMsg->speedE);
511 speedD = XDR_decode_double (PosMsg->speedD);
512 left_aileron = XDR_decode_float (PosMsg->left_aileron);
513 right_aileron = XDR_decode_float (PosMsg->right_aileron);
514 elevator = XDR_decode_float (PosMsg->elevator);
515 rudder = XDR_decode_float (PosMsg->rudder);
516 /*for (int i = 0; i < 6; i++) {
517 rpms[i] = XDR_decode_float (PosMsg->rpms[i]);
519 rateH = XDR_decode_float (PosMsg->rateH);
520 rateR = XDR_decode_float (PosMsg->rateR);
521 rateP = XDR_decode_float (PosMsg->rateP);
522 accN = XDR_decode_float (PosMsg->accN);
523 accE = XDR_decode_float (PosMsg->accE);
524 accD = XDR_decode_float (PosMsg->accD);
526 //////////////////////////////////////////////////
527 // Check if the player is already in the game
528 // by using the Callsign
529 //////////////////////////////////////////////////
530 for (CurrentPlayer = m_MPClientList.begin ();
531 CurrentPlayer != m_MPClientList.end ();
534 if ((*CurrentPlayer)->CompareCallsign(MsgHdr->Callsign))
536 // Player found. Update the data for the player if the timestamp is OK
537 if ((*CurrentPlayer)->CheckTime(time, timeusec))
538 (*CurrentPlayer)->SetPosition(lat, lon, alt,
540 speedN, speedE, speedD,
541 left_aileron, right_aileron, elevator, rudder,
550 { // nothing more to do
553 //////////////////////////////////////////////////
554 // Player not active, so add as new player
555 //////////////////////////////////////////////////
557 NewPlayer = new MPPlayer;
558 NewPlayer->Open(SenderAddress.getHost(), MsgHdr->ReplyPort,
559 MsgHdr->Callsign, PosMsg->Model, false);
560 if (NewPlayer->CheckTime(time, timeusec))
561 NewPlayer->SetPosition(lat, lon, alt,
563 speedN, speedE, speedD,
564 left_aileron, right_aileron, elevator, rudder,
568 m_MPClientList.push_back (NewPlayer);
570 // if we have a new player then we need to send all our properties
571 send_all_props = true;
573 } // FGMultiplayMgr::ProcessPosMsg()
574 //////////////////////////////////////////////////////////////////////
576 //////////////////////////////////////////////////////////////////////
578 // handle a chat message
579 // FIXME: display chat message withi flightgear
581 //////////////////////////////////////////////////////////////////////
583 FGMultiplayMgr::ProcessChatMsg
586 netAddress & SenderAddress
589 T_ChatMsg* ChatMsg; // Pointer to chat message in received data
590 T_MsgHdr* MsgHdr; // Pointer to header in received data
592 MsgHdr = (T_MsgHdr *)Msg;
593 if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + 1)
595 SG_LOG( SG_NETWORK, SG_ALERT,
596 "FGMultiplayMgr::MP_ProcessData - "
597 << "Chat message received with insufficient data" );
601 char *MsgBuf = new char[MsgHdr->MsgLen - sizeof(T_MsgHdr)];
602 strncpy(MsgBuf, ((T_ChatMsg *)(Msg + sizeof(T_MsgHdr)))->Text, MsgHdr->MsgLen - sizeof(T_MsgHdr));
603 MsgBuf[MsgHdr->MsgLen - sizeof(T_MsgHdr) - 1] = '\0';
605 ChatMsg = (T_ChatMsg *)(Msg + sizeof(T_MsgHdr));
606 SG_LOG ( SG_NETWORK, SG_ALERT,
607 "Chat [" << MsgHdr->Callsign << "]" << " " << MsgBuf << endl);
609 } // FGMultiplayMgr::ProcessChatMsg ()
610 //////////////////////////////////////////////////////////////////////
612 //////////////////////////////////////////////////////////////////////
614 // handle a property message
616 //////////////////////////////////////////////////////////////////////
617 void FGMultiplayMgr::ProcessPropMsg ( const char *Msg, netAddress & SenderAddress )
619 T_PropertyMsg* PropMsg; // Pointer to the message itself
620 T_MsgHdr* MsgHdr; // Pointer to the message header
621 t_MPClientListIterator CurrentPlayer;
623 MsgHdr = (T_MsgHdr *) Msg;
625 if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + sizeof(T_PropertyMsg)) {
626 SG_LOG( SG_NETWORK, SG_ALERT,
627 "FGMultiplayMgr::MP_ProcessData - "
628 << "Properties message received with insufficient data" );
630 } else if (MsgHdr->MsgLen > sizeof(T_MsgHdr) + sizeof(T_PropertyMsg)) {
631 SG_LOG( SG_NETWORK, SG_ALERT,
632 "FGMultiplayMgr::MP_ProcessData - "
633 << "Properties message received with more data than I know how to handle" );
637 PropMsg = (T_PropertyMsg *)(Msg + sizeof(T_MsgHdr));
639 //////////////////////////////////////////////////
640 // Check if the player is already in the game
641 // by using the Callsign, but don't activate
642 // new players (they need to send a position
644 //////////////////////////////////////////////////
645 for (CurrentPlayer = m_MPClientList.begin ();
646 CurrentPlayer != m_MPClientList.end ();
649 if ((*CurrentPlayer)->CompareCallsign(MsgHdr->Callsign))
651 // Player found. Update the data for the player.
652 (*CurrentPlayer)->SetProperty(PropMsg->property,
653 (SGPropertyNode::Type) XDR_decode_uint32(PropMsg->type),
654 XDR_decode_double(PropMsg->val));
655 SG_LOG( SG_NETWORK, SG_INFO,
656 "FGMultiplayMgr::MP_ProcessData - "
658 << (SGPropertyNode::Type) XDR_decode_uint32(PropMsg->type)
659 << XDR_decode_double(PropMsg->val) );
664 //////////////////////////////////////////////////////////////////////
666 // For each active player, tell the player object
667 // to update its position on the scene. If a player object
668 // returns status information indicating that it has not
669 // had an update for some time then the player is deleted.
671 //////////////////////////////////////////////////////////////////////
673 FGMultiplayMgr::Update (void)
675 MPPlayer::TPlayerDataState ePlayerDataState;
676 t_MPClientListIterator CurrentPlayer;
678 CurrentPlayer = m_MPClientList.begin ();
679 while (CurrentPlayer != m_MPClientList.end ())
681 ePlayerDataState = (*CurrentPlayer)->Draw();
682 //////////////////////////////////////////////////
683 // If the player has not received an update for
684 // some time then assume that the player has quit.
685 //////////////////////////////////////////////////
686 if (ePlayerDataState == MPPlayer::PLAYER_DATA_EXPIRED)
688 SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::Update - "
689 << "Deleting player from game. Callsign: "
690 << (*CurrentPlayer)->Callsign() );
691 t_MPClientListIterator P;
695 CurrentPlayer = m_MPClientList.erase (P);
697 else CurrentPlayer++;
699 } // FGMultiplayMgr::Update()
700 //////////////////////////////////////////////////////////////////////
702 #endif // FG_MPLAYER_AS