]> git.mxchange.org Git - flightgear.git/blob - src/MultiPlayer/mpplayer.cxx
returning addresses of auto vars is *dangerous* (ask Vasilii! :-)
[flightgear.git] / src / MultiPlayer / mpplayer.cxx
1 //////////////////////////////////////////////////////////////////////
2 //
3 // mpplayer.cxx -- routines for a player within a multiplayer Flightgear
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 //////////////////////////////////////////////////////////////////////
26
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30
31 #ifdef FG_MPLAYER_AS
32
33 /******************************************************************
34 * $Id$
35 *
36 * Description: Provides a container for a player in a multiplayer
37 * game. The players network address, model, callsign and positoin
38 * are held. When the player is created and open called, the player's
39 * model is loaded onto the scene. The position transform matrix
40 * is updated by calling SetPosition. When Draw is called the
41 * elapsed time since the last update is checked. If the model
42 * position information has been updated in the last TIME_TO_LIVE
43 * seconds then the model position is updated on the scene.
44 *
45 ******************************************************************/
46
47 #include "mpplayer.hxx"
48
49 #include <stdlib.h>
50 #if !(defined(_MSC_VER) || defined(__MINGW32__))
51 #   include <netdb.h>
52 #   include <sys/socket.h>
53 #   include <netinet/in.h>
54 #   include <arpa/inet.h>
55 #endif
56 #include <plib/netSocket.h>
57 #include <plib/sg.h>
58
59 #include <simgear/scene/model/modellib.hxx>
60 #include <simgear/scene/model/placementtrans.hxx>
61
62 #include <Main/globals.hxx>
63 #include <Scenery/scenery.hxx>
64
65
66 // These constants are provided so that the ident command can list file versions.
67 const char sMPPLAYER_BID[] = "$Id$";
68 const char sMPPLAYER_HID[] = MPPLAYER_HID;
69
70 //////////////////////////////////////////////////////////////////////
71 //
72 //  constructor
73 //
74 //////////////////////////////////////////////////////////////////////
75 MPPlayer::MPPlayer()
76 {
77     m_Initialised   = false;
78     m_LastUpdate    = 0;
79     m_Callsign      = "none";
80     m_PlayerAddress.set("localhost", 0);
81 } // MPPlayer::MPPlayer()
82 //////////////////////////////////////////////////////////////////////
83
84 /******************************************************************
85 * Name: ~MPPlayer
86 * Description: Destructor.
87 ******************************************************************/
88 MPPlayer::~MPPlayer() 
89 {
90     Close();
91 }
92
93 /******************************************************************
94 * Name: Open
95 * Description: Initialises class.
96 ******************************************************************/
97 bool 
98 MPPlayer::Open
99     (
100     const string &Address,
101     const int &Port,
102     const string &Callsign,
103     const string &ModelName,
104     bool LocalPlayer
105     )
106 {
107     bool Success = true;
108
109     if (!m_Initialised)
110     {
111         m_PlayerAddress.set(Address.c_str(), Port);
112         m_Callsign = Callsign;
113         m_ModelName = ModelName;
114         m_LocalPlayer = LocalPlayer;
115         SG_LOG( SG_NETWORK, SG_ALERT, "Initialising " << m_Callsign
116            << " using '" << m_ModelName << "'" );
117         // If the player is remote then load the model
118         if (!LocalPlayer)
119         {
120              try {
121                  LoadModel();
122              } catch (...) {
123                  SG_LOG( SG_NETWORK, SG_ALERT,
124                    "Failed to load remote model '" << ModelName << "'." );
125                  return false;
126              }
127         }
128         m_Initialised = Success;
129     }
130     else
131     {
132         SG_LOG( SG_NETWORK, SG_ALERT, "MPPlayer::Open - "
133           << "Attempt to open an already opened player connection." );
134         Success = false;
135     }
136     /* Return true if open succeeds */
137     return Success;
138 }
139
140 /******************************************************************
141 * Name: Close
142 * Description: Resets the object.
143 ******************************************************************/
144 void
145 MPPlayer::Close(void)
146 {
147     // Remove the model from the game
148     if (m_Initialised && !m_LocalPlayer) 
149     {
150         // Disconnect the model from the transform,
151         // then the transform from the scene.
152         m_ModelTrans->removeKid(m_Model);
153         globals->get_scenery()->unregister_placement_transform(m_ModelTrans);
154         globals->get_scenery()->get_aircraft_branch()->removeKid( m_ModelTrans);
155         // Flush the model loader so that it erases the model from its list of
156         // models.
157         // globals->get_model_lib()->flush1();
158         // Assume that plib/ssg deletes the model and transform as their
159         // refcounts should be zero.
160     }
161     m_Initialised   = false;
162     m_Updated       = false;
163     m_LastUpdate    = 0;
164     m_Callsign      = "none";
165 }
166
167 /******************************************************************
168 * Name: SetPosition
169 * Description: Updates position data held for this player and resets
170 * the last update time.
171 ******************************************************************/
172 void
173 MPPlayer::SetPosition
174     (
175     const sgQuat PlayerOrientation,
176     const sgdVec3 PlayerPosition
177     )
178 {
179     // Save the position matrix and update time
180     if (m_Initialised)
181     {
182         sgdCopyVec3(m_ModelPosition, PlayerPosition);
183         sgCopyVec4(m_ModelOrientation, PlayerOrientation);
184         time(&m_LastUpdate);
185         m_Updated = true;
186     }
187 }
188
189 /******************************************************************
190 * Name: Draw
191 * Description: Updates the position for the player's model
192 * The state of the player's data is returned.
193 ******************************************************************/
194 MPPlayer::TPlayerDataState
195 MPPlayer::Draw (void)
196 {
197     MPPlayer::TPlayerDataState eResult = PLAYER_DATA_NOT_AVAILABLE;
198     if (m_Initialised && !m_LocalPlayer) 
199     {
200         if ((time(NULL) - m_LastUpdate < TIME_TO_LIVE))
201         {
202             // Peform an update if it has changed since the last update
203             if (m_Updated)
204             {
205                 // Transform and update player model
206                 sgMat4 orMat;
207                 sgMakeIdentMat4(orMat);
208                 sgQuatToMatrix(orMat, m_ModelOrientation);
209                 m_ModelTrans->setTransform(m_ModelPosition, orMat);
210                 eResult = PLAYER_DATA_AVAILABLE;
211                 // Clear the updated flag so that the position data
212                 // is only available if it has changed
213                 m_Updated = false;
214             }
215         }
216         else
217         {   // Data has not been updated for some time.
218             eResult = PLAYER_DATA_EXPIRED;
219         }
220     }
221     return eResult;
222 }
223
224 /******************************************************************
225 * Name: Callsign
226 * Description: Returns the player's callsign.
227 ******************************************************************/
228 string
229 MPPlayer::Callsign(void) const
230 {
231     return m_Callsign;
232 }
233
234 /******************************************************************
235 * Name: CompareCallsign
236 * Description: Returns true if the player's callsign matches
237 * the given callsign.
238 ******************************************************************/
239 bool
240 MPPlayer::CompareCallsign(const char *Callsign) const 
241 {
242     return (m_Callsign == Callsign);
243 }
244
245 /******************************************************************
246 * Name: LoadModel
247 * Description: Loads the player's aircraft model.
248 ******************************************************************/
249 void
250 MPPlayer::LoadModel (void)
251 {
252     m_ModelTrans = new ssgPlacementTransform;
253     // Load the model
254     m_Model = globals->get_model_lib()->load_model( globals->get_fg_root(),
255       m_ModelName, globals->get_props(), globals->get_sim_time_sec() );
256     m_Model->clrTraversalMaskBits( SSGTRAV_HOT );
257     // Add model to transform
258     m_ModelTrans->addKid( m_Model );
259     // Place on scene under aircraft branch
260     globals->get_scenery()->get_aircraft_branch()->addKid( m_ModelTrans );
261     globals->get_scenery()->register_placement_transform( m_ModelTrans);
262 }
263
264 /******************************************************************
265 * Name: FillPosMsg
266 * Description: Populates the header and data for a position message.
267 ******************************************************************/
268 void
269 MPPlayer::FillPosMsg
270     (
271     T_MsgHdr *MsgHdr,
272     T_PositionMsg *PosMsg
273     )
274 {
275     FillMsgHdr(MsgHdr, POS_DATA_ID);
276     strncpy(PosMsg->Model, m_ModelName.c_str(), MAX_MODEL_NAME_LEN);
277     PosMsg->Model[MAX_MODEL_NAME_LEN - 1] = '\0';
278     PosMsg->PlayerPosition[0] = XDR_encode_double (m_ModelPosition[0]);
279     PosMsg->PlayerPosition[1] = XDR_encode_double (m_ModelPosition[1]);
280     PosMsg->PlayerPosition[2] = XDR_encode_double (m_ModelPosition[2]);
281     PosMsg->PlayerOrientation[0] = XDR_encode_float (m_ModelOrientation[0]);
282     PosMsg->PlayerOrientation[1] = XDR_encode_float (m_ModelOrientation[1]);
283     PosMsg->PlayerOrientation[2] = XDR_encode_float (m_ModelOrientation[2]);
284     PosMsg->PlayerOrientation[3] = XDR_encode_float (m_ModelOrientation[3]);
285 }
286
287 /******************************************************************
288 * Name: FillMsgHdr
289 * Description: Populates the header of a multiplayer message.
290 ******************************************************************/
291 void
292 MPPlayer::FillMsgHdr
293     (
294     T_MsgHdr *MsgHdr, 
295     const int MsgId
296     )
297 {
298     struct in_addr  address;
299     uint32_t        len;
300
301     switch (MsgId)
302     {
303         case CHAT_MSG_ID:
304             len = sizeof(T_MsgHdr) + sizeof(T_ChatMsg);
305             break;
306         case POS_DATA_ID:
307             len = sizeof(T_MsgHdr) + sizeof(T_PositionMsg);
308             break;
309         default:
310             len = sizeof(T_MsgHdr);
311             break;
312     }
313     MsgHdr->Magic           = XDR_encode_uint32 (MSG_MAGIC);
314     MsgHdr->Version         = XDR_encode_uint32 (PROTO_VER);
315     MsgHdr->MsgId           = XDR_encode_uint32 (MsgId);
316     MsgHdr->MsgLen          = XDR_encode_uint32 (len);
317     // inet_addr returns address in network byte order
318     // no need to encode it
319     MsgHdr->ReplyAddress   = inet_addr( m_PlayerAddress.getHost() );
320     MsgHdr->ReplyPort      = XDR_encode_uint32 (m_PlayerAddress.getPort());
321     strncpy(MsgHdr->Callsign, m_Callsign.c_str(), MAX_CALLSIGN_LEN);
322     MsgHdr->Callsign[MAX_CALLSIGN_LEN - 1] = '\0';
323 }
324
325 #endif // FG_MPLAYER_AS
326