]> git.mxchange.org Git - flightgear.git/blob - src/MultiPlayer/mpplayer.cxx
- don't abort if remote model isn't installed; output missing model's
[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
57 #include <Main/globals.hxx>
58 #include <Scenery/scenery.hxx>
59
60
61 // These constants are provided so that the ident command can list file versions.
62 const char sMPPLAYER_BID[] = "$Id$";
63 const char sMPPLAYER_HID[] = MPPLAYER_HID;
64
65
66 /******************************************************************
67 * Name: MPPlayer
68 * Description: Constructor.
69 ******************************************************************/
70 MPPlayer::MPPlayer() {
71
72     // Initialise private members
73     m_bInitialised = false;
74     m_LastUpdate = 0;
75     m_PlayerAddress.set("localhost", 0);
76     m_sCallsign = "none";
77
78
79 }
80
81
82 /******************************************************************
83 * Name: ~MPPlayer
84 * Description: Destructor.
85 ******************************************************************/
86 MPPlayer::~MPPlayer() {
87
88     Close();
89
90 }
91
92
93 /******************************************************************
94 * Name: Open
95 * Description: Initialises class.
96 ******************************************************************/
97 bool MPPlayer::Open(const string &sAddress, const int &iPort, const string &sCallsign, const string &sModelName, bool bLocalPlayer) {
98
99     bool bSuccess = true;
100
101     if (!m_bInitialised) {
102
103         m_PlayerAddress.set(sAddress.c_str(), iPort);
104         m_sCallsign = sCallsign;
105         m_sModelName = sModelName;
106         m_bLocalPlayer = bLocalPlayer;
107
108         // If the player is remote then load the model
109         if (!bLocalPlayer) {
110              try {
111                  LoadModel();
112              } catch (...) {
113                  SG_LOG( SG_NETWORK, SG_ALERT, "Failed to load remote model '" << sModelName << "'." );
114                  return false;
115              }
116         }
117
118         m_bInitialised = bSuccess;
119
120     } else {
121         SG_LOG( SG_NETWORK, SG_ALERT, "MPPlayer::Open - Attempt to open an already open player connection." );
122         bSuccess = false;
123     }
124
125
126     /* Return true if open succeeds */
127     return bSuccess;
128
129 }
130
131
132 /******************************************************************
133 * Name: Close
134 * Description: Resets the object.
135 ******************************************************************/
136 void MPPlayer::Close(void) {
137
138     // Remove the model from the game
139     if (m_bInitialised && !m_bLocalPlayer) {
140
141         // Disconnect the model from the transform, then the transform from the scene.
142         m_ModelTrans->removeKid(m_Model);
143         globals->get_scenery()->get_aircraft_branch()->removeKid( m_ModelTrans);
144
145         // Flush the model loader so that it erases the model from its list of
146         // models.
147         globals->get_model_lib()->flush1();
148
149         // Assume that plib/ssg deletes the model and transform as their
150         // refcounts should be zero.
151
152     }
153
154     m_bInitialised = false;
155     m_bUpdated = false;
156     m_LastUpdate = 0;
157     m_sCallsign = "none";
158
159 }
160
161
162 /******************************************************************
163 * Name: SetPosition
164 * Description: Updates position data held for this player and resets
165 * the last update time.
166 ******************************************************************/
167 void MPPlayer::SetPosition(const sgMat4 PlayerPosMat4) {
168
169
170     // Save the position matrix and update time
171     if (m_bInitialised) {
172         sgCopyMat4(m_ModelPos, PlayerPosMat4);
173         time(&m_LastUpdate);
174         m_bUpdated = true;
175     }
176
177
178 }
179
180
181 /******************************************************************
182 * Name: Draw
183 * Description: Updates the position for the player's model
184 * The state of the player's data is returned.
185 ******************************************************************/
186 MPPlayer::TPlayerDataState MPPlayer::Draw(void) {
187
188     MPPlayer::TPlayerDataState eResult = PLAYER_DATA_NOT_AVAILABLE;
189
190     sgCoord sgPlayerCoord;
191
192     if (m_bInitialised && !m_bLocalPlayer) {
193         if ((time(NULL) - m_LastUpdate < TIME_TO_LIVE)) {
194             // Peform an update if it has changed since the last update
195             if (m_bUpdated) {
196
197                 // Transform and update player model
198                 sgSetCoord( &sgPlayerCoord, m_ModelPos);
199                 m_ModelTrans->setTransform( &sgPlayerCoord );
200
201                 eResult = PLAYER_DATA_AVAILABLE;
202
203                 // Clear the updated flag so that the position data
204                 // is only available if it has changed
205                 m_bUpdated = false;
206             }
207
208         // Data has not been updated for some time.
209         } else {
210             eResult = PLAYER_DATA_EXPIRED;
211         }
212
213     }
214
215     return eResult;
216
217 }
218
219
220 /******************************************************************
221 * Name: Callsign
222 * Description: Returns the player's callsign.
223 ******************************************************************/
224 string MPPlayer::Callsign(void) const {
225
226     return m_sCallsign;
227
228 }
229
230
231 /******************************************************************
232 * Name: CompareCallsign
233 * Description: Returns true if the player's callsign matches
234 * the given callsign.
235 ******************************************************************/
236 bool MPPlayer::CompareCallsign(const char *sCallsign) const {
237
238     return (m_sCallsign == sCallsign);
239
240 }
241
242
243 /******************************************************************
244 * Name: LoadModel
245 * Description: Loads the player's aircraft model.
246 ******************************************************************/
247 void MPPlayer::LoadModel(void) {
248
249
250     m_ModelTrans = new ssgTransform;
251
252     // Load the model
253     m_Model = globals->get_model_lib()->load_model( globals->get_fg_root(),
254                                                     m_sModelName,
255                                                     globals->get_props(),
256                                                     globals->get_sim_time_sec() );
257     m_Model->clrTraversalMaskBits( SSGTRAV_HOT );
258
259     // Add model to transform
260     m_ModelTrans->addKid( m_Model );
261
262     // Optimise model and transform
263     ssgFlatten( m_Model );
264     ssgStripify( m_ModelTrans );
265
266     // Place on scene under aircraft branch
267     globals->get_scenery()->get_aircraft_branch()->addKid( m_ModelTrans );
268
269
270 }
271
272
273 /******************************************************************
274 * Name: FillPosMsg
275 * Description: Populates the header and data for a position message.
276 ******************************************************************/
277 void MPPlayer::FillPosMsg(T_MsgHdr *MsgHdr, T_PositionMsg *PosMsg) {
278
279     FillMsgHdr(MsgHdr, POS_DATA_ID);
280
281     strncpy(PosMsg->sModel, m_sModelName.c_str(), MAX_MODEL_NAME_LEN);
282     PosMsg->sModel[MAX_MODEL_NAME_LEN - 1] = '\0';
283     sgCopyMat4(PosMsg->PlayerPos, m_ModelPos);
284
285
286 }
287
288
289 /******************************************************************
290 * Name: FillMsgHdr
291 * Description: Populates the header of a multiplayer message.
292 ******************************************************************/
293 void MPPlayer::FillMsgHdr(T_MsgHdr *MsgHdr, const int iMsgId) {
294
295     struct in_addr address;
296
297     MsgHdr->MsgId = iMsgId;
298
299     switch (iMsgId) {
300         case CHAT_MSG_ID:
301             MsgHdr->iMsgLen = sizeof(T_MsgHdr) + sizeof(T_ChatMsg);
302             break;
303         case POS_DATA_ID:
304             MsgHdr->iMsgLen = sizeof(T_MsgHdr) + sizeof(T_PositionMsg);
305             break;
306         default:
307             MsgHdr->iMsgLen = sizeof(T_MsgHdr);
308             break;
309     }
310
311     address.s_addr = inet_addr( m_PlayerAddress.getHost() );
312     MsgHdr->lReplyAddress = address.s_addr;
313
314     MsgHdr->iReplyPort = m_PlayerAddress.getPort();
315
316     strncpy(MsgHdr->sCallsign, m_sCallsign.c_str(), MAX_CALLSIGN_LEN);
317     MsgHdr->sCallsign[MAX_CALLSIGN_LEN - 1] = '\0';
318
319
320 }
321
322 #endif // FG_MPLAYER_AS
323