]> git.mxchange.org Git - flightgear.git/blob - src/MultiPlayer/mpplayer.cxx
Better cross platform compatibility.
[flightgear.git] / src / MultiPlayer / mpplayer.cxx
1 // mpplayer.cxx -- routines for a player within a multiplayer Flightgear
2 //
3 // Written by Duncan McCreanor, started February 2003.
4 // duncan.mccreanor@airservicesaustralia.com
5 //
6 // Copyright (C) 2003  Airservices Australia
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of the
11 // License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 // General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 //
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #ifdef FG_MPLAYER_AS
28
29 /******************************************************************
30 * $Id$
31 *
32 * Description: Provides a container for a player in a multiplayer
33 * game. The players network address, model, callsign and positoin
34 * are held. When the player is created and open called, the player's
35 * model is loaded onto the scene. The position transform matrix
36 * is updated by calling SetPosition. When Draw is called the
37 * elapsed time since the last update is checked. If the model
38 * position information has been updated in the last TIME_TO_LIVE
39 * seconds then the model position is updated on the scene.
40 *
41 ******************************************************************/
42
43 #include "mpplayer.hxx"
44
45 #include <stdlib.h>
46 #if !(defined(_MSC_VER) || defined(__MINGW32__))
47 # include <netdb.h>
48 # include <sys/socket.h>
49 # include <netinet/in.h>
50 # include <arpa/inet.h>
51 #endif
52 #include <plib/netSocket.h>
53 #include <plib/sg.h>
54
55 #include <simgear/scene/model/modellib.hxx>
56 #include <simgear/scene/model/placementtrans.hxx>
57
58 #include <Main/globals.hxx>
59 #include <Scenery/scenery.hxx>
60
61
62 // These constants are provided so that the ident command can list file versions.
63 const char sMPPLAYER_BID[] = "$Id$";
64 const char sMPPLAYER_HID[] = MPPLAYER_HID;
65
66
67 /******************************************************************
68 * Name: MPPlayer
69 * Description: Constructor.
70 ******************************************************************/
71 MPPlayer::MPPlayer() {
72
73     // Initialise private members
74     m_bInitialised = false;
75     m_LastUpdate = 0;
76     m_PlayerAddress.set("localhost", 0);
77     m_sCallsign = "none";
78
79
80 }
81
82
83 /******************************************************************
84 * Name: ~MPPlayer
85 * Description: Destructor.
86 ******************************************************************/
87 MPPlayer::~MPPlayer() {
88
89     Close();
90
91 }
92
93
94 /******************************************************************
95 * Name: Open
96 * Description: Initialises class.
97 ******************************************************************/
98 bool MPPlayer::Open(const string &sAddress, const int &iPort, const string &sCallsign, const string &sModelName, bool bLocalPlayer) {
99
100     bool bSuccess = true;
101
102     if (!m_bInitialised) {
103
104         m_PlayerAddress.set(sAddress.c_str(), iPort);
105         m_sCallsign = sCallsign;
106         m_sModelName = sModelName;
107         m_bLocalPlayer = bLocalPlayer;
108         SG_LOG( SG_NETWORK, SG_ALERT, "Initialising " << m_sCallsign
109            << " using '" << m_sModelName << "'" );
110
111         // If the player is remote then load the model
112         if (!bLocalPlayer) {
113              try {
114                  LoadModel();
115              } catch (...) {
116                  SG_LOG( SG_NETWORK, SG_ALERT, "Failed to load remote model '" << sModelName << "'." );
117                  return false;
118              }
119         }
120
121         m_bInitialised = bSuccess;
122
123     } else {
124         SG_LOG( SG_NETWORK, SG_ALERT, "MPPlayer::Open - Attempt to open an already open player connection." );
125         bSuccess = false;
126     }
127
128
129     /* Return true if open succeeds */
130     return bSuccess;
131
132 }
133
134
135 /******************************************************************
136 * Name: Close
137 * Description: Resets the object.
138 ******************************************************************/
139 void MPPlayer::Close(void) {
140
141     // Remove the model from the game
142     if (m_bInitialised && !m_bLocalPlayer) {
143
144         // Disconnect the model from the transform, then the transform from the scene.
145         m_ModelTrans->removeKid(m_Model);
146         globals->get_scenery()->unregister_placement_transform(m_ModelTrans);
147         globals->get_scenery()->get_aircraft_branch()->removeKid( m_ModelTrans);
148
149         // Flush the model loader so that it erases the model from its list of
150         // models.
151 //        globals->get_model_lib()->flush1();
152
153         // Assume that plib/ssg deletes the model and transform as their
154         // refcounts should be zero.
155
156     }
157
158     m_bInitialised = false;
159     m_bUpdated = false;
160     m_LastUpdate = 0;
161     m_sCallsign = "none";
162
163 }
164
165
166 /******************************************************************
167 * Name: SetPosition
168 * Description: Updates position data held for this player and resets
169 * the last update time.
170 ******************************************************************/
171 void MPPlayer::SetPosition(const sgQuat PlayerOrientation,
172                            const sgdVec3 PlayerPosition) {
173
174     // Save the position matrix and update time
175     if (m_bInitialised) {
176         sgdCopyVec3(m_ModelPosition, PlayerPosition);
177         sgCopyVec4(m_ModelOrientation, PlayerOrientation);
178         time(&m_LastUpdate);
179         m_bUpdated = true;
180     }
181
182 }
183
184
185 /******************************************************************
186 * Name: Draw
187 * Description: Updates the position for the player's model
188 * The state of the player's data is returned.
189 ******************************************************************/
190 MPPlayer::TPlayerDataState MPPlayer::Draw(void) {
191
192     MPPlayer::TPlayerDataState eResult = PLAYER_DATA_NOT_AVAILABLE;
193
194     if (m_bInitialised && !m_bLocalPlayer) {
195         if ((time(NULL) - m_LastUpdate < TIME_TO_LIVE)) {
196             // Peform an update if it has changed since the last update
197             if (m_bUpdated) {
198
199                 // Transform and update player model
200                 sgMat4 orMat;
201                 sgMakeIdentMat4(orMat);
202                 sgQuatToMatrix(orMat, m_ModelOrientation);
203                 m_ModelTrans->setTransform(m_ModelPosition, orMat);
204
205                 eResult = PLAYER_DATA_AVAILABLE;
206
207                 // Clear the updated flag so that the position data
208                 // is only available if it has changed
209                 m_bUpdated = false;
210             }
211
212         // Data has not been updated for some time.
213         } else {
214             eResult = PLAYER_DATA_EXPIRED;
215         }
216
217     }
218
219     return eResult;
220
221 }
222
223
224 /******************************************************************
225 * Name: Callsign
226 * Description: Returns the player's callsign.
227 ******************************************************************/
228 string MPPlayer::Callsign(void) const {
229
230     return m_sCallsign;
231
232 }
233
234
235 /******************************************************************
236 * Name: CompareCallsign
237 * Description: Returns true if the player's callsign matches
238 * the given callsign.
239 ******************************************************************/
240 bool MPPlayer::CompareCallsign(const char *sCallsign) const {
241
242     return (m_sCallsign == sCallsign);
243
244 }
245
246
247 /******************************************************************
248 * Name: LoadModel
249 * Description: Loads the player's aircraft model.
250 ******************************************************************/
251 void MPPlayer::LoadModel(void) {
252
253
254     m_ModelTrans = new ssgPlacementTransform;
255
256     // Load the model
257     m_Model = globals->get_model_lib()->load_model( globals->get_fg_root(),
258                                                     m_sModelName,
259                                                     globals->get_props(),
260                                                     globals->get_sim_time_sec() );
261     m_Model->clrTraversalMaskBits( SSGTRAV_HOT );
262
263     // Add model to transform
264     m_ModelTrans->addKid( m_Model );
265
266     // Place on scene under aircraft branch
267     globals->get_scenery()->get_aircraft_branch()->addKid( m_ModelTrans );
268     globals->get_scenery()->register_placement_transform( m_ModelTrans);
269
270
271 }
272
273
274 /******************************************************************
275 * Name: FillPosMsg
276 * Description: Populates the header and data for a position message.
277 ******************************************************************/
278 void MPPlayer::FillPosMsg(T_MsgHdr *MsgHdr, T_PositionMsg *PosMsg) {
279
280     FillMsgHdr(MsgHdr, POS_DATA_ID);
281
282     strncpy(PosMsg->sModel, m_sModelName.c_str(), MAX_MODEL_NAME_LEN);
283     PosMsg->sModel[MAX_MODEL_NAME_LEN - 1] = '\0';
284     /*
285     sgdCopyVec3(PosMsg->PlayerPosition, m_ModelPosition);
286     sgCopyQuat(PosMsg->PlayerOrientation, m_ModelOrientation);
287     */
288     PosMsg->PlayerPosition[0] = XDR_encode_double (m_ModelPosition[0]);
289     PosMsg->PlayerPosition[1] = XDR_encode_double (m_ModelPosition[1]);
290     PosMsg->PlayerPosition[2] = XDR_encode_double (m_ModelPosition[2]);
291     PosMsg->PlayerOrientation[0] = XDR_encode_float (m_ModelOrientation[0]);
292     PosMsg->PlayerOrientation[1] = XDR_encode_float (m_ModelOrientation[1]);
293     PosMsg->PlayerOrientation[2] = XDR_encode_float (m_ModelOrientation[2]);
294     PosMsg->PlayerOrientation[3] = XDR_encode_float (m_ModelOrientation[3]);
295 }
296
297
298 /******************************************************************
299 * Name: FillMsgHdr
300 * Description: Populates the header of a multiplayer message.
301 ******************************************************************/
302 void MPPlayer::FillMsgHdr(T_MsgHdr *MsgHdr, const int iMsgId) {
303
304     struct in_addr  address;
305     uint32_t        len;
306
307     switch (iMsgId) {
308         case CHAT_MSG_ID:
309             len = sizeof(T_MsgHdr) + sizeof(T_ChatMsg);
310             break;
311         case POS_DATA_ID:
312             len = sizeof(T_MsgHdr) + sizeof(T_PositionMsg);
313             break;
314         default:
315             len = sizeof(T_MsgHdr);
316             break;
317     }
318     MsgHdr->Magic           = XDR_encode_uint32 (MSG_MAGIC);
319     MsgHdr->Version         = XDR_encode_uint32 (PROTO_VER);
320     MsgHdr->MsgId           = XDR_encode_uint32 (iMsgId);
321     MsgHdr->iMsgLen         = XDR_encode_uint32 (len);
322     // inet_addr returns address in network byte order
323     // no need to encode it
324     MsgHdr->lReplyAddress   = inet_addr( m_PlayerAddress.getHost() );
325     MsgHdr->iReplyPort      = XDR_encode_uint32 (m_PlayerAddress.getPort());
326
327     strncpy(MsgHdr->sCallsign, m_sCallsign.c_str(), MAX_CALLSIGN_LEN);
328     MsgHdr->sCallsign[MAX_CALLSIGN_LEN - 1] = '\0';
329
330 }
331
332 #endif // FG_MPLAYER_AS
333