]> git.mxchange.org Git - flightgear.git/blob - src/MultiPlayer/multiplaymgr.cxx
6e17075587c630eb69777bb664759253cbadf82c
[flightgear.git] / src / MultiPlayer / multiplaymgr.cxx
1 //////////////////////////////////////////////////////////////////////
2 //
3 // multiplaymgr.hpp
4 //
5 // Written by Duncan McCreanor, started February 2003.
6 // duncan.mccreanor@airservicesaustralia.com
7 //
8 // Copyright (C) 2003  Airservices Australia
9 // Copyright (C) 2005  Oliver Schroeder
10 //
11 // This program is free software; you can redistribute it and/or
12 // modify it under the terms of the GNU General Public License as
13 // published by the Free Software Foundation; either version 2 of the
14 // License, or (at your option) any later version.
15 //
16 // This program is distributed in the hope that it will be useful, but
17 // WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 // General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 //
25 // $Id$
26 //  
27 //////////////////////////////////////////////////////////////////////
28
29 #ifdef HAVE_CONFIG_H
30 #include <config.h>
31 #endif
32
33 #ifdef FG_MPLAYER_AS
34
35 #include <sys/types.h>
36 #if !(defined(_MSC_VER) || defined(__MINGW32__))
37 #   include <sys/socket.h>
38 #   include <netinet/in.h>
39 #   include <arpa/inet.h>
40 #endif
41 #include <stdlib.h>
42
43 #include <plib/netSocket.h>
44
45 #include <simgear/debug/logstream.hxx>
46
47 #include <Main/fg_props.hxx>
48 #include "multiplaymgr.hxx"
49 #include "mpmessages.hxx"
50 #include "mpplayer.hxx"
51
52 #define MAX_PACKET_SIZE 1024
53
54 // These constants are provided so that the ident 
55 // command can list file versions
56 const char sMULTIPLAYMGR_BID[] = 
57     "$Id$";
58 const char sMULTIPLAYMGR_HID[] = MULTIPLAYTXMGR_HID;
59
60 //////////////////////////////////////////////////////////////////////
61 //
62 //  MultiplayMgr constructor
63 //
64 //////////////////////////////////////////////////////////////////////
65 FGMultiplayMgr::FGMultiplayMgr() 
66 {
67     m_Initialised   = false;
68     m_LocalPlayer   = NULL;
69     m_RxAddress     = "0";
70     m_RxPort        = 0;
71     m_Initialised   = false;
72     m_HaveServer    = false;
73 } // FGMultiplayMgr::FGMultiplayMgr()
74 //////////////////////////////////////////////////////////////////////
75
76 //////////////////////////////////////////////////////////////////////
77 //
78 //  MultiplayMgr destructor
79 //
80 //////////////////////////////////////////////////////////////////////
81 FGMultiplayMgr::~FGMultiplayMgr() 
82 {
83     Close();
84 } // FGMultiplayMgr::~FGMultiplayMgr()
85 //////////////////////////////////////////////////////////////////////
86
87 //////////////////////////////////////////////////////////////////////
88 //
89 //  Initialise object
90 //
91 //////////////////////////////////////////////////////////////////////
92 bool
93 FGMultiplayMgr::init (void) 
94 {
95     string  TxAddress;      // Destination address
96     int     TxPort;
97
98     //////////////////////////////////////////////////
99     //  Initialise object if not already done
100     //////////////////////////////////////////////////
101     if (m_Initialised) 
102     {
103         SG_LOG( SG_NETWORK, SG_WARN,
104           "FGMultiplayMgr::init - already initialised" );
105         return (false);
106     }
107     //////////////////////////////////////////////////
108     //  Set members from property values
109     //////////////////////////////////////////////////
110     TxAddress      = fgGetString   ("/sim/multiplay/txhost");
111     TxPort         = fgGetInt      ("/sim/multiplay/txport");
112     m_Callsign     = fgGetString   ("/sim/multiplay/callsign");
113     m_RxAddress    = fgGetString   ("/sim/multiplay/rxhost");
114     m_RxPort       = fgGetInt      ("/sim/multiplay/rxport");
115     if (m_RxPort <= 0)
116     {
117         m_RxPort = 5000;
118     }
119     if (m_Callsign == "")
120     {
121         // FIXME: use getpwuid
122         m_Callsign = "JohnDoe"; 
123     }
124     if (m_RxAddress == "")
125     {
126         m_RxAddress = "127.0.0.1";
127     }
128     if ((TxPort > 0) && (TxAddress != ""))
129     {
130         m_HaveServer = true;
131         m_Server.set (TxAddress.c_str(), TxPort); 
132     }
133     SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-txaddress= "<<TxAddress);
134     SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-txport= "<<TxPort );
135     SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-rxaddress="<<m_RxAddress );
136     SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-rxport= "<<m_RxPort);
137     SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-callsign= "<<m_Callsign);
138     m_DataSocket = new netSocket();
139     if (!m_DataSocket->open(false))
140     {
141         SG_LOG( SG_NETWORK, SG_ALERT,
142           "FGMultiplayMgr::init - Failed to create data socket" );
143         return (false);
144     }
145     m_DataSocket->setBlocking(false);
146     m_DataSocket->setBroadcast(true);
147     if (m_DataSocket->bind(m_RxAddress.c_str(), m_RxPort) != 0)
148     {
149         perror("bind");
150         SG_LOG( SG_NETWORK, SG_ALERT,
151           "FGMultiplayMgr::Open - Failed to bind receive socket" );
152         return (false);
153     }
154     m_LocalPlayer = new MPPlayer();
155     if (!m_LocalPlayer->Open(m_RxAddress, m_RxPort, m_Callsign,
156                             fgGetString("/sim/model/path"), true)) 
157     {
158         SG_LOG( SG_NETWORK, SG_ALERT,
159           "FGMultiplayMgr::init - Failed to create local player" );
160         return (false);
161     }
162     m_Initialised = true;
163     return (true);
164 } // FGMultiplayMgr::init()
165 //////////////////////////////////////////////////////////////////////
166
167 //////////////////////////////////////////////////////////////////////
168 //
169 //  Closes and deletes the local player object. Closes
170 //  and deletes the tx socket. Resets the object state to unitialised.
171 //
172 //////////////////////////////////////////////////////////////////////
173 void
174 FGMultiplayMgr::Close (void) 
175 {
176     //////////////////////////////////////////////////
177     //  Delete local player
178     //////////////////////////////////////////////////
179     if (m_LocalPlayer) 
180     {
181         delete m_LocalPlayer;
182         m_LocalPlayer = NULL;
183     }
184     //////////////////////////////////////////////////
185     //  Delete any existing players
186     //////////////////////////////////////////////////
187     t_MPClientListIterator CurrentPlayer;
188     t_MPClientListIterator P;
189     CurrentPlayer  = m_MPClientList.begin ();
190     while (CurrentPlayer != m_MPClientList.end ())
191     {
192         P = CurrentPlayer;
193         delete (*P);
194         *P = 0;
195         CurrentPlayer = m_MPClientList.erase (P);
196     }
197     //////////////////////////////////////////////////
198     //  Delete socket
199     //////////////////////////////////////////////////
200     if (m_DataSocket) 
201     {
202         m_DataSocket->close();
203         delete m_DataSocket;
204         m_DataSocket = NULL;
205     }
206     m_Initialised = false;
207 } // FGMultiplayMgr::Close(void)
208 //////////////////////////////////////////////////////////////////////
209
210 //////////////////////////////////////////////////////////////////////
211 //
212 //  Description: Sends the position data for the local position.
213 //
214 //////////////////////////////////////////////////////////////////////
215 void
216 FGMultiplayMgr::SendMyPosition
217     (
218     const sgQuat PlayerOrientation,
219     const sgdVec3 PlayerPosition
220     )
221 {
222     T_MsgHdr        MsgHdr;
223     T_PositionMsg   PosMsg;
224     char Msg[sizeof(T_MsgHdr) + sizeof(T_PositionMsg)];
225
226     if ((! m_Initialised) || (! m_HaveServer))
227     {
228         if (! m_Initialised)
229         SG_LOG( SG_NETWORK, SG_ALERT,
230           "FGMultiplayMgr::SendMyPosition - not initialised" );
231         if (! m_HaveServer)
232         SG_LOG( SG_NETWORK, SG_ALERT,
233           "FGMultiplayMgr::SendMyPosition - no server" );
234         return;
235     }
236     m_LocalPlayer->SetPosition(PlayerOrientation, PlayerPosition);
237     m_LocalPlayer->FillPosMsg(&MsgHdr, &PosMsg);
238     memcpy(Msg, &MsgHdr, sizeof(T_MsgHdr));
239     memcpy(Msg + sizeof(T_MsgHdr), &PosMsg, sizeof(T_PositionMsg));
240     m_DataSocket->sendto (Msg,
241       sizeof(T_MsgHdr) + sizeof(T_PositionMsg), 0, &m_Server);
242 } // FGMultiplayMgr::SendMyPosition()
243 //////////////////////////////////////////////////////////////////////
244
245 //////////////////////////////////////////////////////////////////////
246 //
247 //  Name: SendTextMessage
248 //  Description: Sends a message to the player. The message must
249 //  contain a valid and correctly filled out header and optional
250 //  message body.
251 //
252 //////////////////////////////////////////////////////////////////////
253 void
254 FGMultiplayMgr::SendTextMessage
255     (
256     const string &MsgText
257     ) const
258 {
259     T_MsgHdr    MsgHdr;
260     T_ChatMsg   ChatMsg;
261     unsigned int iNextBlockPosition = 0;
262     char Msg[sizeof(T_MsgHdr) + sizeof(T_ChatMsg)];
263
264     if ((! m_Initialised) || (! m_HaveServer))
265     {
266         return;
267     }
268     m_LocalPlayer->FillMsgHdr(&MsgHdr, CHAT_MSG_ID);
269     //////////////////////////////////////////////////
270     // Divide the text string into blocks that fit
271     // in the message and send the blocks.
272     //////////////////////////////////////////////////
273     while (iNextBlockPosition < MsgText.length()) 
274     {
275         strncpy (ChatMsg.Text, 
276           MsgText.substr(iNextBlockPosition, MAX_CHAT_MSG_LEN - 1).c_str(),
277           MAX_CHAT_MSG_LEN);
278         ChatMsg.Text[MAX_CHAT_MSG_LEN - 1] = '\0';
279         memcpy (Msg, &MsgHdr, sizeof(T_MsgHdr));
280         memcpy (Msg + sizeof(T_MsgHdr), &ChatMsg, sizeof(T_ChatMsg));
281         m_DataSocket->sendto (Msg,
282           sizeof(T_MsgHdr) + sizeof(T_ChatMsg), 0, &m_Server);
283         iNextBlockPosition += MAX_CHAT_MSG_LEN - 1;
284     }
285 } // FGMultiplayMgr::SendTextMessage ()
286 //////////////////////////////////////////////////////////////////////
287
288 //////////////////////////////////////////////////////////////////////
289 //
290 //  Name: ProcessData
291 //  Description: Processes data waiting at the receive socket. The
292 //  processing ends when there is no more data at the socket.
293 //  
294 //////////////////////////////////////////////////////////////////////
295 void
296 FGMultiplayMgr::ProcessData (void) 
297 {
298     char        Msg[MAX_PACKET_SIZE];   // Buffer for received message
299     int         Bytes;                  // Bytes received
300     T_MsgHdr*   MsgHdr;                 // Pointer to header in received data
301     netAddress  SenderAddress;
302
303     if (! m_Initialised)
304     {
305         SG_LOG( SG_NETWORK, SG_ALERT,
306           "FGMultiplayMgr::ProcessData - not initialised" );
307         return;
308     }
309     //////////////////////////////////////////////////
310     //  Read the receive socket and process any data
311     //////////////////////////////////////////////////
312     do {
313         //////////////////////////////////////////////////
314         //  Although the recv call asks for 
315         //  MAX_PACKET_SIZE of data, the number of bytes
316         //  returned will only be that of the next
317         //  packet waiting to be processed.
318         //////////////////////////////////////////////////
319         Bytes = m_DataSocket->recvfrom (Msg, MAX_PACKET_SIZE, 0,
320                                          &SenderAddress);
321         //////////////////////////////////////////////////
322         //  no Data received
323         //////////////////////////////////////////////////
324         if (Bytes <= 0)
325         {
326             if (errno != EAGAIN)
327             {
328                 perror("FGMultiplayMgr::MP_ProcessData");
329             }
330             return;
331         }
332         if (Bytes <= (int)sizeof(MsgHdr)) 
333         {
334             SG_LOG( SG_NETWORK, SG_ALERT,
335               "FGMultiplayMgr::MP_ProcessData - "
336               << "received message with insufficient data" );
337             return;
338         }
339         //////////////////////////////////////////////////
340         //  Read header
341         //////////////////////////////////////////////////
342         MsgHdr = (T_MsgHdr *)Msg;
343         MsgHdr->Magic       = XDR_decode_uint32 (MsgHdr->Magic);
344         MsgHdr->Version     = XDR_decode_uint32 (MsgHdr->Version);
345         MsgHdr->MsgId       = XDR_decode_uint32 (MsgHdr->MsgId);
346         MsgHdr->MsgLen      = XDR_decode_uint32 (MsgHdr->MsgLen);
347         MsgHdr->ReplyPort   = XDR_decode_uint32 (MsgHdr->ReplyPort);
348         if (MsgHdr->Magic != MSG_MAGIC)
349         {
350             SG_LOG( SG_NETWORK, SG_ALERT,
351               "FGMultiplayMgr::MP_ProcessData - "
352               << "message has invalid magic number!" );
353         }
354         if (MsgHdr->Version != PROTO_VER) 
355         {
356             SG_LOG( SG_NETWORK, SG_ALERT,
357               "FGMultiplayMgr::MP_ProcessData - "
358               << "message has invalid protocoll number!" );
359         }
360         //////////////////////////////////////////////////
361         //  Process the player data unless we generated it
362         //////////////////////////////////////////////////
363         if (m_Callsign == MsgHdr->Callsign) 
364         {
365             return;
366         }
367         //////////////////////////////////////////////////
368         //  Process messages
369         //////////////////////////////////////////////////
370         switch(MsgHdr->MsgId)
371         {
372             case CHAT_MSG_ID:
373                 ProcessChatMsg ((char*) & Msg, SenderAddress);
374                 break;
375             case POS_DATA_ID:
376                 ProcessPosMsg ((char*) & Msg, SenderAddress);
377                 break;
378             default:
379                 SG_LOG( SG_NETWORK, SG_ALERT,
380                   "FGMultiplayMgr::MP_ProcessData - "
381                   << "Unknown message Id received: " 
382                   << MsgHdr->MsgId );
383                 break;
384         } // switch
385     } while (Bytes > 0);
386 } // FGMultiplayMgr::ProcessData(void)
387 //////////////////////////////////////////////////////////////////////
388
389 //////////////////////////////////////////////////////////////////////
390 //
391 //  handle a position message
392 //
393 //////////////////////////////////////////////////////////////////////
394 void
395 FGMultiplayMgr::ProcessPosMsg
396     (
397     const char *Msg,
398     netAddress & SenderAddress
399     )
400 {
401     T_PositionMsg*  PosMsg;     // Pointer to position message in received data
402     T_MsgHdr*       MsgHdr;     // Pointer to header in received data
403     bool            ActivePlayer; 
404     sgQuat          Orientation;
405     sgdVec3         Position;
406     t_MPClientListIterator CurrentPlayer;
407
408     ActivePlayer = false;
409     MsgHdr = (T_MsgHdr *)Msg;
410     if (MsgHdr->MsgLen != sizeof(T_MsgHdr) + sizeof(T_PositionMsg))
411     {
412         SG_LOG( SG_NETWORK, SG_ALERT,
413           "FGMultiplayMgr::MP_ProcessData - "
414           << "Position message received with insufficient data" );
415         return;
416     }
417     PosMsg = (T_PositionMsg *)(Msg + sizeof(T_MsgHdr));
418     Position[0] = XDR_decode_double (PosMsg->PlayerPosition[0]);
419     Position[1] = XDR_decode_double (PosMsg->PlayerPosition[1]);
420     Position[2] = XDR_decode_double (PosMsg->PlayerPosition[2]);
421     Orientation[0] = XDR_decode_float (PosMsg->PlayerOrientation[0]);
422     Orientation[1] = XDR_decode_float (PosMsg->PlayerOrientation[1]);
423     Orientation[2] = XDR_decode_float (PosMsg->PlayerOrientation[2]);
424     Orientation[3] = XDR_decode_float (PosMsg->PlayerOrientation[3]);
425     //////////////////////////////////////////////////
426     //  Check if the player is already in the game 
427     //  by using the Callsign
428     //////////////////////////////////////////////////
429     for (CurrentPlayer  = m_MPClientList.begin ();
430          CurrentPlayer != m_MPClientList.end ();
431          CurrentPlayer++)
432     {
433         if ((*CurrentPlayer)->CompareCallsign(MsgHdr->Callsign))
434         {
435             // Player found. Update the data for the player.
436             (*CurrentPlayer)->SetPosition(Orientation, Position);
437             ActivePlayer = true;
438         }
439     } // for (...)
440     if (ActivePlayer) 
441     {   // nothing more to do
442         return;
443     }
444     //////////////////////////////////////////////////
445     //  Player not active, so add as new player
446     //////////////////////////////////////////////////
447     MPPlayer* NewPlayer;
448     NewPlayer = new MPPlayer;
449     NewPlayer->Open(SenderAddress.getHost(), MsgHdr->ReplyPort,
450         MsgHdr->Callsign, PosMsg->Model, false);
451     NewPlayer->SetPosition(Orientation, Position);
452     m_MPClientList.push_back (NewPlayer);
453 } // FGMultiplayMgr::ProcessPosMsg()
454 //////////////////////////////////////////////////////////////////////
455
456 //////////////////////////////////////////////////////////////////////
457 //
458 //  handle a chat message
459 //  FIXME: display chat message withi flightgear
460 //
461 //////////////////////////////////////////////////////////////////////
462 void
463 FGMultiplayMgr::ProcessChatMsg
464     (
465     const char *Msg,
466     netAddress & SenderAddress
467     )
468 {
469     T_ChatMsg*  ChatMsg;    // Pointer to chat message in received data
470     T_MsgHdr*   MsgHdr;     // Pointer to header in received data
471
472     MsgHdr = (T_MsgHdr *)Msg;
473     if (MsgHdr->MsgLen != sizeof(T_MsgHdr) + sizeof(T_ChatMsg))
474     {
475         SG_LOG( SG_NETWORK, SG_ALERT,
476           "FGMultiplayMgr::MP_ProcessData - "
477           << "Chat message received with insufficient data" );
478         return;
479     }
480     ChatMsg = (T_ChatMsg *)(Msg + sizeof(T_MsgHdr));
481     SG_LOG ( SG_NETWORK, SG_ALERT, 
482       "Chat [" << MsgHdr->Callsign << "]" << " " << ChatMsg->Text << endl);
483 } // FGMultiplayMgr::ProcessChatMsg ()
484 //////////////////////////////////////////////////////////////////////
485
486 //////////////////////////////////////////////////////////////////////
487 //
488 //  For each active player, tell the player object
489 //  to update its position on the scene. If a player object
490 //  returns status information indicating that it has not
491 //  had an update for some time then the player is deleted.
492 //
493 //////////////////////////////////////////////////////////////////////
494 void
495 FGMultiplayMgr::Update (void) 
496 {
497     MPPlayer::TPlayerDataState ePlayerDataState;
498     t_MPClientListIterator CurrentPlayer;
499
500     CurrentPlayer  = m_MPClientList.begin ();
501     while (CurrentPlayer != m_MPClientList.end ())
502     {
503         ePlayerDataState = (*CurrentPlayer)->Draw();
504         //////////////////////////////////////////////////
505         // If the player has not received an update for
506         // some time then assume that the player has quit.
507         //////////////////////////////////////////////////
508         if (ePlayerDataState == MPPlayer::PLAYER_DATA_EXPIRED) 
509         {
510             SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::Update - "
511               << "Deleting player from game. Callsign: "
512               << (*CurrentPlayer)->Callsign() );
513             t_MPClientListIterator P;
514                 P = CurrentPlayer;
515                 delete (*P);
516             *P = 0;
517             CurrentPlayer = m_MPClientList.erase (P);
518         }
519         else    CurrentPlayer++;
520     }
521 } // FGMultiplayMgr::Update()
522 //////////////////////////////////////////////////////////////////////
523
524 #endif // FG_MPLAYER_AS
525