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