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