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
477 struct in_addr PlayerAddress;
479 double lat, lon, alt;
480 double hdg, roll, pitch;
481 double speedN, speedE, speedD;
482 double left_aileron, right_aileron, elevator, rudder;
484 double rateH, rateR, rateP;
485 double accN, accE, accD;
486 t_MPClientListIterator CurrentPlayer;
488 ActivePlayer = false;
489 MsgHdr = (T_MsgHdr *)Msg;
490 if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + sizeof(T_PositionMsg))
492 SG_LOG( SG_NETWORK, SG_ALERT,
493 "FGMultiplayMgr::MP_ProcessData - "
494 << "Position message received with insufficient data" );
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" );
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]);
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);
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 ();
535 if ((*CurrentPlayer)->CompareCallsign(MsgHdr->Callsign))
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,
541 speedN, speedE, speedD,
542 left_aileron, right_aileron, elevator, rudder,
551 { // nothing more to do
554 //////////////////////////////////////////////////
555 // Player not active, so add as new player
556 //////////////////////////////////////////////////
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,
564 speedN, speedE, speedD,
565 left_aileron, right_aileron, elevator, rudder,
569 m_MPClientList.push_back (NewPlayer);
571 // if we have a new player then we need to send all our properties
572 send_all_props = true;
574 } // FGMultiplayMgr::ProcessPosMsg()
575 //////////////////////////////////////////////////////////////////////
577 //////////////////////////////////////////////////////////////////////
579 // handle a chat message
580 // FIXME: display chat message withi flightgear
582 //////////////////////////////////////////////////////////////////////
584 FGMultiplayMgr::ProcessChatMsg
587 netAddress & SenderAddress
590 T_ChatMsg* ChatMsg; // Pointer to chat message in received data
591 T_MsgHdr* MsgHdr; // Pointer to header in received data
593 MsgHdr = (T_MsgHdr *)Msg;
594 if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + 1)
596 SG_LOG( SG_NETWORK, SG_ALERT,
597 "FGMultiplayMgr::MP_ProcessData - "
598 << "Chat message received with insufficient data" );
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';
606 ChatMsg = (T_ChatMsg *)(Msg + sizeof(T_MsgHdr));
607 SG_LOG ( SG_NETWORK, SG_ALERT,
608 "Chat [" << MsgHdr->Callsign << "]" << " " << MsgBuf << endl);
610 } // FGMultiplayMgr::ProcessChatMsg ()
611 //////////////////////////////////////////////////////////////////////
613 //////////////////////////////////////////////////////////////////////
615 // handle a property message
617 //////////////////////////////////////////////////////////////////////
618 void FGMultiplayMgr::ProcessPropMsg ( const char *Msg, netAddress & SenderAddress )
620 T_PropertyMsg* PropMsg; // Pointer to the message itself
621 T_MsgHdr* MsgHdr; // Pointer to the message header
622 t_MPClientListIterator CurrentPlayer;
624 MsgHdr = (T_MsgHdr *) Msg;
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" );
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" );
638 PropMsg = (T_PropertyMsg *)(Msg + sizeof(T_MsgHdr));
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
645 //////////////////////////////////////////////////
646 for (CurrentPlayer = m_MPClientList.begin ();
647 CurrentPlayer != m_MPClientList.end ();
650 if ((*CurrentPlayer)->CompareCallsign(MsgHdr->Callsign))
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 - "
659 << (SGPropertyNode::Type) XDR_decode_uint32(PropMsg->type)
660 << XDR_decode_double(PropMsg->val) );
665 //////////////////////////////////////////////////////////////////////
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.
672 //////////////////////////////////////////////////////////////////////
674 FGMultiplayMgr::Update (void)
676 MPPlayer::TPlayerDataState ePlayerDataState;
677 t_MPClientListIterator CurrentPlayer;
679 CurrentPlayer = m_MPClientList.begin ();
680 while (CurrentPlayer != m_MPClientList.end ())
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)
689 SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::Update - "
690 << "Deleting player from game. Callsign: "
691 << (*CurrentPlayer)->Callsign() );
692 t_MPClientListIterator P;
696 CurrentPlayer = m_MPClientList.erase (P);
698 else CurrentPlayer++;
700 } // FGMultiplayMgr::Update()
701 //////////////////////////////////////////////////////////////////////
703 #endif // FG_MPLAYER_AS