]> git.mxchange.org Git - flightgear.git/blob - src/MultiPlayer/multiplayrxmgr.cxx
Add includes for MacOS X
[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
25 /******************************************************************
26 * $Id$
27 *
28 * Description: The multiplayer rx manager provides control over
29 * multiplayer data reception and processing for an interactive
30 * multiplayer FlightGear simulation.
31 *
32 * The objects that hold player information are accessed via
33 * a fixed size array. A fixed array is used since it provides
34 * speed benefits over working with linked lists and is easier
35 * to code. Also, there is no point allowing for an unlimited
36 * number of players as too many players will slow the game
37 * down to the point where it is unplayable.
38 *
39 * When player position data is received, the callsign of
40 * the player is checked against existing players. If the
41 * player does not exist, a new player is created in the
42 * next free slot of the player array. If the player does
43 * exist, the player's positional matrix is updated.
44 *
45 * The Update method is used to move the players on the
46 * scene. The return value from calling MPPlayer::Draw
47 * indicates the state of the player. If data for a player
48 * has not been received data for some time, the player object
49 * is deleted and the array element freed.
50 *
51 ******************************************************************/
52
53 #include <sys/types.h>
54 #include <sys/socket.h>
55 #include <netinet/in.h>
56 #include <arpa/inet.h>
57 #include <plib/netSocket.h>
58 #include <stdlib.h>
59 #include <Main/fg_props.hxx>
60
61 #include "multiplayrxmgr.hxx"
62 #include "mpmessages.hxx"
63 #include "mpplayer.hxx"
64
65 #define MAX_PACKET_SIZE 1024
66
67 // These constants are provided so that the ident command can list file versions.
68 const char sMULTIPLAYTXMGR_BID[] = "$Id$";
69 const char sMULTIPLAYRXMGR_HID[] = MULTIPLAYRXMGR_HID;
70
71
72
73 /******************************************************************
74 * Name: FGMultiplayRxMgr
75 * Description: Constructor.
76 ******************************************************************/
77 FGMultiplayRxMgr::FGMultiplayRxMgr() {
78
79     int iPlayerCnt;         // Count of players in player array
80
81     // Initialise private members
82     m_sRxAddress = "0";
83     m_iRxPort = 0;
84     m_bInitialised = false;
85
86     // Clear the player array
87     for (iPlayerCnt = 0; iPlayerCnt < MAX_PLAYERS; iPlayerCnt++) {
88         m_Player[iPlayerCnt] = NULL;
89     }
90
91 }
92
93
94 /******************************************************************
95 * Name: ~FGMultiplayRxMgr
96 * Description: Destructor. Closes and deletes objects owned by
97 * this object.
98 ******************************************************************/
99 FGMultiplayRxMgr::~FGMultiplayRxMgr() {
100
101     Close();
102
103 }
104
105
106 /******************************************************************
107 * Name: init
108 * Description: Initialises multiplayer receive.
109 ******************************************************************/
110 bool FGMultiplayRxMgr::init(void) {
111
112     bool bSuccess = true;                       // Result of initialisation
113
114     // Initialise object if not already done
115     if (!m_bInitialised) {
116
117         // Set members from property values
118         m_sCallsign = fgGetString("/sim/multiplay/callsign");
119         m_sRxAddress = fgGetString("/sim/multiplay/rxhost");
120         m_iRxPort = fgGetInt("/sim/multiplay/rxport");
121
122         cout << "FGMultiplayRxMgr::init - rxaddress= " << m_sRxAddress << endl;
123         cout << "FGMultiplayRxMgr::init - rxport= " << m_iRxPort << endl;
124         cout << "FGMultiplayRxMgr::init - callsign= " << m_sCallsign << endl;
125
126         // Create and open rx socket
127         mDataRxSocket = new netSocket();
128         if (!mDataRxSocket->open(false)) {
129             // Failed to open rx socket
130             cerr << "FGMultiplayRxMgr::Open - Failed to create data receive socket" << endl;
131             bSuccess = false;
132         } else {
133
134             // Configure the socket
135             mDataRxSocket->setBlocking(false);
136             mDataRxSocket->setBroadcast(true);
137             if (mDataRxSocket->bind(m_sRxAddress.c_str(), m_iRxPort) != 0) {
138                 perror("bind");
139                 // Failed to bind
140                 cerr << "FGMultiplayRxMgr::Open - Failed to bind receive socket" << endl;
141                 bSuccess = false;
142             }
143
144         }
145
146         // Save manager state
147         m_bInitialised = bSuccess;
148
149     } else {
150         cerr << "FGMultiplayRxMgr::OpenRx - Receiver open requested when receiver is already open" << endl;
151         bSuccess = false;
152     }
153
154     /* Return true if open succeeds */
155     return bSuccess;
156
157 }
158
159
160 /******************************************************************
161 * Name: Close
162 * Description: Closes and deletes and player connections. Closes
163 * and deletes the rx socket. Resets the object state
164 * to unitialised.
165 ******************************************************************/
166 void FGMultiplayRxMgr::Close(void) {
167
168     int iPlayerCnt;         // Count of players in player array
169
170     // Delete any existing players
171     for (iPlayerCnt = 0; iPlayerCnt < MAX_PLAYERS; iPlayerCnt++) {
172         if (m_Player[iPlayerCnt] != NULL) {
173             delete m_Player[iPlayerCnt];
174             m_Player[iPlayerCnt] = NULL;
175         }
176     }
177
178     // Delete socket
179     if (mDataRxSocket) {
180         mDataRxSocket->close();
181         delete mDataRxSocket;
182         mDataRxSocket = NULL;
183     }
184
185     m_bInitialised = false;
186
187 }
188
189
190 /******************************************************************
191 * Name: ProcessData
192 * Description: Processes data waiting at the receive socket. The
193 * processing ends when there is no more data at the socket.
194 ******************************************************************/
195 void FGMultiplayRxMgr::ProcessData(void) {
196
197     char sMsg[MAX_PACKET_SIZE];         // Buffer for received message
198     int iBytes;                         // Bytes received
199     T_MsgHdr *MsgHdr;                   // Pointer to header in received data
200     T_ChatMsg *ChatMsg;                 // Pointer to chat message in received data
201     T_PositionMsg *PosMsg;              // Pointer to position message in received data
202     char *sIpAddress;                   // Address information from header
203     char *sModelName;                   // Model that the remote player is using
204     char *sCallsign;                    // Callsign of the remote player
205     struct in_addr PlayerAddress;       // Used for converting the player's address into dot notation
206     int iPlayerCnt;                     // Count of players in player array
207     bool bActivePlayer = false;         // The state of the player that sent the data
208     int iPort;                          // Port that the remote player receives on
209
210
211     if (m_bInitialised) {
212
213         // Read the receive socket and process any data
214         do {
215
216             // Although the recv call asks for MAX_PACKET_SIZE of data,
217             // the number of bytes returned will only be that of the next
218             // packet waiting to be processed.
219             iBytes = mDataRxSocket->recv(sMsg, MAX_PACKET_SIZE, 0);
220
221             // Data received
222             if (iBytes > 0) {
223                 if (iBytes >= sizeof(MsgHdr)) {
224
225                     // Read header
226                     MsgHdr = (T_MsgHdr *)sMsg;
227                     PlayerAddress.s_addr = MsgHdr->lReplyAddress;
228                     sIpAddress = inet_ntoa(PlayerAddress);
229                     iPort = MsgHdr->iReplyPort;
230                     sCallsign = MsgHdr->sCallsign;
231
232                     // Process the player data unless we generated it
233                     if (m_sCallsign != string(MsgHdr->sCallsign)) {
234
235
236                         // Process messages
237                         switch(MsgHdr->MsgId) {
238                             case CHAT_MSG_ID:
239                                 if (MsgHdr->iMsgLen == sizeof(T_MsgHdr) + sizeof(T_ChatMsg)) {
240                                     ChatMsg = (T_ChatMsg *)(sMsg + sizeof(T_MsgHdr));
241                                     cout << "Chat [" << MsgHdr->sCallsign << "]" << " " << ChatMsg->sText << endl;
242                                 } else {
243                                     cerr << "FGMultiplayRxMgr::MP_ProcessData - Chat message received with insufficient data" << endl;
244                                 }
245                                 break;
246
247                             case POS_DATA_ID:
248                                 if (MsgHdr->iMsgLen == sizeof(T_MsgHdr) + sizeof(T_PositionMsg)) {
249                                     PosMsg = (T_PositionMsg *)(sMsg + sizeof(T_MsgHdr));
250                                     sModelName = PosMsg->sModel;
251
252                                     // Check if the player is already in the game by using the Callsign.
253                                     bActivePlayer = false;
254                                     for (iPlayerCnt = 0; iPlayerCnt < MAX_PLAYERS; iPlayerCnt++) {
255                                         if (m_Player[iPlayerCnt] != NULL) {
256                                             if (m_Player[iPlayerCnt]->CompareCallsign(MsgHdr->sCallsign)) {
257
258                                                 // Player found. Update the data for the player.
259                                                 m_Player[iPlayerCnt]->SetPosition(PosMsg->PlayerPos);
260                                                 bActivePlayer = true;
261
262                                             }
263                                         }
264                                     }
265
266                                     // Player not active, so add as new player
267                                     if (!bActivePlayer) {
268                                         iPlayerCnt = 0;
269                                         do {
270                                             if (m_Player[iPlayerCnt] == NULL) {
271                                                 cout << "FGMultiplayRxMgr::ProcessRxData - Add new player. IP: " << sIpAddress
272                                                     << ", Call: " <<  sCallsign << ", model: " << sModelName << endl;
273                                                 m_Player[iPlayerCnt] = new MPPlayer;
274                                                 m_Player[iPlayerCnt]->Open(string(sIpAddress), iPort, string(sCallsign), string(sModelName), false);
275                                                 m_Player[iPlayerCnt]->SetPosition(PosMsg->PlayerPos);
276                                                 bActivePlayer = true;
277                                             }
278                                             iPlayerCnt++;
279                                         } while (iPlayerCnt < MAX_PLAYERS && !bActivePlayer);
280
281                                         // Check if the player was added
282                                         if (!bActivePlayer) {
283                                             if (iPlayerCnt == MAX_PLAYERS) {
284                                                 cerr << "FGMultiplayRxMgr::MP_ProcessData - Unable to add new player (" << sCallsign << "). Too many players." << endl;
285                                             }
286                                         }
287                                     }
288
289                                 } else {
290                                     cerr << "FGMultiplayRxMgr::MP_ProcessData - Position message received with insufficient data" << endl;
291                                 }
292                                 break;
293
294                             default:
295                                 cerr << "FGMultiplayRxMgr::MP_ProcessData - Unknown message Id received: " << MsgHdr->MsgId << endl;
296                                 break;
297
298
299                         }
300                     }
301                 }
302
303
304             // Error or no data
305             } else if (iBytes == -1) {
306                 if (errno != EAGAIN) {
307                     perror("FGMultiplayRxMgr::MP_ProcessData");
308                 }
309             }
310
311         } while (iBytes > 0);
312
313     }
314
315
316 }
317
318
319 /******************************************************************
320 * Name: Update
321 * Description: For each active player, tell the player object
322 * to update its position on the scene. If a player object
323 * returns status information indicating that it has not
324 * had an update for some time then the player is deleted.
325 ******************************************************************/
326 void FGMultiplayRxMgr::Update(void) {
327
328     int iPlayerDataState;
329     int iPlayerId;
330
331     for (iPlayerId = 0; iPlayerId < MAX_PLAYERS; iPlayerId++) {
332         if (m_Player[iPlayerId] != NULL) {
333             iPlayerDataState = m_Player[iPlayerId]->Draw();
334
335             // If the player has not received an update for some
336             // time then assume that the player has quit.
337             if (iPlayerDataState == PLAYER_DATA_EXPIRED) {
338                 delete m_Player[iPlayerId];
339                 m_Player[iPlayerId] = NULL;
340             }
341
342         }
343     }
344
345 }
346
347