]> git.mxchange.org Git - flightgear.git/blob - src/MultiPlayer/multiplayrxmgr.cxx
Oliver Schroeder:
[flightgear.git] / src / MultiPlayer / multiplayrxmgr.cxx
1 // multiplayrxmgr.cxx -- routines for receiving multiplayer data
2 //                       for Flightgear
3 //
4 // Written by Duncan McCreanor, started February 2003.
5 // duncan.mccreanor@airservicesaustralia.com
6 //
7 // Copyright (C) 2003  Airservices Australia
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License as
11 // published by the Free Software Foundation; either version 2 of the
12 // License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful, but
15 // WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 // General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with this program; if not, write to the Free Software
21 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 //
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #ifdef FG_MPLAYER_AS
29
30 /******************************************************************
31 * $Id$
32 *
33 * Description: The multiplayer rx manager provides control over
34 * multiplayer data reception and processing for an interactive
35 * multiplayer FlightGear simulation.
36 *
37 * The objects that hold player information are accessed via
38 * a fixed size array. A fixed array is used since it provides
39 * speed benefits over working with linked lists and is easier
40 * to code. Also, there is no point allowing for an unlimited
41 * number of players as too many players will slow the game
42 * down to the point where it is unplayable.
43 *
44 * When player position data is received, the callsign of
45 * the player is checked against existing players. If the
46 * player does not exist, a new player is created in the
47 * next free slot of the player array. If the player does
48 * exist, the player's positional matrix is updated.
49 *
50 * The Update method is used to move the players on the
51 * scene. The return value from calling MPPlayer::Draw
52 * indicates the state of the player. If data for a player
53 * has not been received data for some time, the player object
54 * is deleted and the array element freed.
55 *
56 ******************************************************************/
57
58 #include <sys/types.h>
59 #if !(defined(_MSC_VER) || defined(__MINGW32__))
60 # include <sys/socket.h>
61 # include <netinet/in.h>
62 # include <arpa/inet.h>
63 #endif
64 #include <plib/netSocket.h>
65 #include <stdlib.h>
66
67 #include <simgear/debug/logstream.hxx>
68 #include <Main/fg_props.hxx>
69
70 #include "multiplayrxmgr.hxx"
71 #include "mpmessages.hxx"
72 #include "mpplayer.hxx"
73
74 #define MAX_PACKET_SIZE 1024
75
76 // These constants are provided so that the ident command can list file versions.
77 const char sMULTIPLAYTXMGR_BID[] = "$Id$";
78 const char sMULTIPLAYRXMGR_HID[] = MULTIPLAYRXMGR_HID;
79
80
81
82 /******************************************************************
83 * Name: FGMultiplayRxMgr
84 * Description: Constructor.
85 ******************************************************************/
86 FGMultiplayRxMgr::FGMultiplayRxMgr() {
87
88     int iPlayerCnt;         // Count of players in player array
89
90     // Initialise private members
91     m_sRxAddress = "0";
92     m_iRxPort = 0;
93     m_bInitialised = false;
94
95     // Clear the player array
96     for (iPlayerCnt = 0; iPlayerCnt < MAX_PLAYERS; iPlayerCnt++) {
97         m_Player[iPlayerCnt] = NULL;
98     }
99
100 }
101
102
103 /******************************************************************
104 * Name: ~FGMultiplayRxMgr
105 * Description: Destructor. Closes and deletes objects owned by
106 * this object.
107 ******************************************************************/
108 FGMultiplayRxMgr::~FGMultiplayRxMgr() {
109
110     Close();
111
112 }
113
114
115 /******************************************************************
116 * Name: init
117 * Description: Initialises multiplayer receive.
118 ******************************************************************/
119 bool FGMultiplayRxMgr::init(void) {
120
121     bool bSuccess = true;                       // Result of initialisation
122
123     // Initialise object if not already done
124     if (!m_bInitialised) {
125
126         // Set members from property values
127         m_sCallsign = fgGetString("/sim/multiplay/callsign");
128         m_sRxAddress = fgGetString("/sim/multiplay/rxhost");
129         m_iRxPort = fgGetInt("/sim/multiplay/rxport");
130
131         SG_LOG( SG_NETWORK, SG_INFO, "FGMultiplayRxMgr::init - rxaddress= "
132                                      << m_sRxAddress );
133         SG_LOG( SG_NETWORK, SG_INFO, "FGMultiplayRxMgr::init - rxport= "
134                                      << m_iRxPort);
135         SG_LOG( SG_NETWORK, SG_INFO, "FGMultiplayRxMgr::init - callsign= "
136                                      << m_sCallsign );
137
138
139         // Create and open rx socket
140         mDataRxSocket = new netSocket();
141         if (!mDataRxSocket->open(false)) {
142             // Failed to open rx socket
143             SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayRxMgr::Open - Failed to create data receive socket" );
144             bSuccess = false;
145         } else {
146
147             // Configure the socket
148             mDataRxSocket->setBlocking(false);
149             mDataRxSocket->setBroadcast(true);
150             if (mDataRxSocket->bind(m_sRxAddress.c_str(), m_iRxPort) != 0) {
151                 perror("bind");
152                 // Failed to bind
153                 SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayRxMgr::Open - Failed to bind receive socket" );
154                 bSuccess = false;
155             }
156
157         }
158
159         // Save manager state
160         m_bInitialised = bSuccess;
161
162     } else {
163         SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayRxMgr::OpenRx - Receiver open requested when receiver is already open" );
164         bSuccess = false;
165     }
166
167     /* Return true if open succeeds */
168     return bSuccess;
169
170 }
171
172
173 /******************************************************************
174 * Name: Close
175 * Description: Closes and deletes and player connections. Closes
176 * and deletes the rx socket. Resets the object state
177 * to unitialised.
178 ******************************************************************/
179 void FGMultiplayRxMgr::Close(void) {
180
181     int iPlayerCnt;         // Count of players in player array
182
183     // Delete any existing players
184     for (iPlayerCnt = 0; iPlayerCnt < MAX_PLAYERS; iPlayerCnt++) {
185         if (m_Player[iPlayerCnt] != NULL) {
186             delete m_Player[iPlayerCnt];
187             m_Player[iPlayerCnt] = NULL;
188         }
189     }
190
191     // Delete socket
192     if (mDataRxSocket) {
193         mDataRxSocket->close();
194         delete mDataRxSocket;
195         mDataRxSocket = NULL;
196     }
197
198     m_bInitialised = false;
199
200 }
201
202
203 /******************************************************************
204 * Name: ProcessData
205 * Description: Processes data waiting at the receive socket. The
206 * processing ends when there is no more data at the socket.
207 ******************************************************************/
208 void FGMultiplayRxMgr::ProcessData(void) {
209
210     char sMsg[MAX_PACKET_SIZE];         // Buffer for received message
211     int iBytes;                         // Bytes received
212     T_MsgHdr *MsgHdr;                   // Pointer to header in received data
213
214
215     if (! m_bInitialised) {
216         return;
217     }
218
219     // Read the receive socket and process any data
220     do {
221
222         // Although the recv call asks for MAX_PACKET_SIZE of data,
223         // the number of bytes returned will only be that of the next
224         // packet waiting to be processed.
225         iBytes = mDataRxSocket->recv(sMsg, MAX_PACKET_SIZE, 0);
226
227         // no Data received
228         if (iBytes <= 0) {
229             if (errno != EAGAIN) {
230                 perror("FGMultiplayRxMgr::MP_ProcessData");
231             }
232             return;
233         }
234         if (iBytes <= (int)sizeof(MsgHdr)) {
235             SG_LOG( SG_NETWORK, SG_ALERT,
236               "FGMultiplayRxMgr::MP_ProcessData - received message with insufficient data" );
237             return;
238         }
239         // Read header
240         MsgHdr = (T_MsgHdr *)sMsg;
241         MsgHdr->Magic           = XDR_decode_uint32 (MsgHdr->Magic);
242         MsgHdr->Version         = XDR_decode_uint32 (MsgHdr->Version);
243         MsgHdr->MsgId           = XDR_decode_uint32 (MsgHdr->MsgId);
244         MsgHdr->iMsgLen         = XDR_decode_uint32 (MsgHdr->iMsgLen);
245         MsgHdr->iReplyPort      = XDR_decode_uint32 (MsgHdr->iReplyPort);
246         if (MsgHdr->Magic != MSG_MAGIC) {
247             SG_LOG( SG_NETWORK, SG_ALERT,
248               "FGMultiplayRxMgr::MP_ProcessData - message has invalid magic number!" );
249         }
250         if (MsgHdr->Version != PROTO_VER) {
251             SG_LOG( SG_NETWORK, SG_ALERT,
252               "FGMultiplayRxMgr::MP_ProcessData - message has invalid protocoll number!" );
253         }
254
255         // Process the player data unless we generated it
256         if (m_sCallsign == MsgHdr->sCallsign) {
257             return;
258         }
259         
260         // Process messages
261         switch(MsgHdr->MsgId) {
262             case CHAT_MSG_ID:
263                 ProcessChatMsg ((char*) & sMsg);
264                 break;
265
266             case POS_DATA_ID:
267                 ProcessPosMsg ((char*) & sMsg);
268                 break;
269
270             default:
271                 SG_LOG( SG_NETWORK, SG_ALERT,
272                   "FGMultiplayRxMgr::MP_ProcessData - Unknown message Id received: " 
273                   << MsgHdr->MsgId );
274                 break;
275         } // switch
276     } while (iBytes > 0);
277
278 }
279
280 void FGMultiplayRxMgr::ProcessPosMsg ( const char *Msg ) {
281
282     T_PositionMsg *PosMsg;              // Pointer to position message in received data
283     T_MsgHdr *MsgHdr;                   // Pointer to header in received data
284     int iPlayerCnt;                     // Count of players in player array
285     bool bActivePlayer = false;         // The state of the player that sent the data
286     sgQuat  Orientation;
287     sgdVec3 Position;
288     char *sIpAddress;                   // Address information from header
289     struct in_addr PlayerAddress;       // Used for converting the player's address into dot notation
290
291     MsgHdr = (T_MsgHdr *)Msg;
292     if (MsgHdr->iMsgLen != sizeof(T_MsgHdr) + sizeof(T_PositionMsg)) {
293         SG_LOG( SG_NETWORK, SG_ALERT,
294           "FGMultiplayRxMgr::MP_ProcessData - Position message received with insufficient data" );
295         return;
296     }
297
298     PosMsg = (T_PositionMsg *)(Msg + sizeof(T_MsgHdr));
299     Position[0] = XDR_decode_double (PosMsg->PlayerPosition[0]);
300     Position[1] = XDR_decode_double (PosMsg->PlayerPosition[1]);
301     Position[2] = XDR_decode_double (PosMsg->PlayerPosition[2]);
302     Orientation[0] = XDR_decode_float (PosMsg->PlayerOrientation[0]);
303     Orientation[1] = XDR_decode_float (PosMsg->PlayerOrientation[1]);
304     Orientation[2] = XDR_decode_float (PosMsg->PlayerOrientation[2]);
305     Orientation[3] = XDR_decode_float (PosMsg->PlayerOrientation[3]);
306
307     // Check if the player is already in the game by using the Callsign.
308     for (iPlayerCnt = 0; iPlayerCnt < MAX_PLAYERS; iPlayerCnt++) {
309         if (m_Player[iPlayerCnt] != NULL) {
310             if (m_Player[iPlayerCnt]->CompareCallsign(MsgHdr->sCallsign)) {
311                 // Player found. Update the data for the player.
312                 m_Player[iPlayerCnt]->SetPosition(Orientation, Position);
313                 bActivePlayer = true;
314             }
315         }
316     }
317
318     if (bActivePlayer) {
319         // nothing more to do
320         return;
321     }
322     // Player not active, so add as new player
323     iPlayerCnt = 0;
324     do {
325         if (m_Player[iPlayerCnt] == NULL) {
326             PlayerAddress.s_addr = MsgHdr->lReplyAddress;       // which is unecoded
327             sIpAddress  = inet_ntoa(PlayerAddress);
328             SG_LOG( SG_NETWORK, SG_ALERT,
329               "FGMultiplayRxMgr::ProcessRxData - Add new player. IP: " << sIpAddress 
330               << ", Call: " <<  MsgHdr->sCallsign 
331               << ", model: " << PosMsg->sModel);
332             m_Player[iPlayerCnt] = new MPPlayer;
333             m_Player[iPlayerCnt]->Open(sIpAddress, MsgHdr->iReplyPort,
334                             MsgHdr->sCallsign, PosMsg->sModel, false);
335             m_Player[iPlayerCnt]->SetPosition(Orientation, Position);
336             bActivePlayer = true;
337         }
338         iPlayerCnt++;
339     } while (iPlayerCnt < MAX_PLAYERS && !bActivePlayer);
340
341     // Check if the player was added
342     if (!bActivePlayer) {
343         if (iPlayerCnt == MAX_PLAYERS) {
344             SG_LOG( SG_NETWORK, SG_ALERT,
345               "FGMultiplayRxMgr::MP_ProcessData - Unable to add new player ("
346               << MsgHdr->sCallsign 
347               << "). Too many players." );
348         }
349     }
350
351 }
352
353 void FGMultiplayRxMgr::ProcessChatMsg ( const char *Msg ) {
354
355     T_ChatMsg *ChatMsg;                 // Pointer to chat message in received data
356     T_MsgHdr *MsgHdr;                   // Pointer to header in received data
357
358     MsgHdr = (T_MsgHdr *)Msg;
359     if (MsgHdr->iMsgLen != sizeof(T_MsgHdr) + sizeof(T_ChatMsg)) {
360         SG_LOG( SG_NETWORK, SG_ALERT,
361                 "FGMultiplayRxMgr::MP_ProcessData - Chat message received with insufficient data" );
362         return;
363     }
364
365     ChatMsg = (T_ChatMsg *)(Msg + sizeof(T_MsgHdr));
366     SG_LOG( SG_NETWORK, SG_BULK, "Chat [" << MsgHdr->sCallsign << "]" << " " << ChatMsg->sText );
367 }
368
369 /******************************************************************
370 * Name: Update
371 * Description: For each active player, tell the player object
372 * to update its position on the scene. If a player object
373 * returns status information indicating that it has not
374 * had an update for some time then the player is deleted.
375 ******************************************************************/
376 void FGMultiplayRxMgr::Update(void) {
377
378     MPPlayer::TPlayerDataState ePlayerDataState;
379     int iPlayerId;
380
381     for (iPlayerId = 0; iPlayerId < MAX_PLAYERS; iPlayerId++) {
382         if (m_Player[iPlayerId] != NULL) {
383             ePlayerDataState = m_Player[iPlayerId]->Draw();
384
385             // If the player has not received an update for some
386             // time then assume that the player has quit.
387             if (ePlayerDataState == MPPlayer::PLAYER_DATA_EXPIRED) {
388                 SG_LOG( SG_NETWORK, SG_BULK, "FGMultiplayRxMgr::Update - Deleting player from game. Callsign: "
389                         << m_Player[iPlayerId]->Callsign() );
390                 delete m_Player[iPlayerId];
391                 m_Player[iPlayerId] = NULL;
392             }
393
394         }
395     }
396
397 }
398
399 #endif // FG_MPLAYER_AS
400