]> git.mxchange.org Git - flightgear.git/blob - src/MultiPlayer/mpplayer.cxx
Portability fix - use SimGear when available
[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 // With additions by Vivian Meazza, January 2006
9 //
10 // Copyright (C) 2003  Airservices Australia
11 // Copyright (C) 2005  Oliver Schroeder
12 //
13 // This program is free software; you can redistribute it and/or
14 // modify it under the terms of the GNU General Public License as
15 // published by the Free Software Foundation; either version 2 of the
16 // License, or (at your option) any later version.
17 //
18 // This program is distributed in the hope that it will be useful, but
19 // WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21 // General Public License for more details.
22 //
23 // You should have received a copy of the GNU General Public License
24 // along with this program; if not, write to the Free Software
25 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 //
27 //////////////////////////////////////////////////////////////////////
28
29 #ifdef HAVE_CONFIG_H
30 #include <config.h>
31 #endif
32
33 #ifdef FG_MPLAYER_AS
34
35 /******************************************************************
36 * $Id$
37 *
38 * Description: Provides a container for a player in a multiplayer
39 * game. The players network address, model, callsign and positoin
40 * are held. When the player is created and open called, the player's
41 * model is loaded onto the scene. The position transform matrix
42 * is updated by calling SetPosition. When Draw is called the
43 * elapsed time since the last update is checked. If the model
44 * position information has been updated in the last TIME_TO_LIVE
45 * seconds then the model position is updated on the scene.
46 *
47 ******************************************************************/
48
49 #include <simgear/timing/timestamp.hxx>
50
51 #include "mpplayer.hxx"
52
53 #include <stdlib.h>
54 #if !(defined(_MSC_VER) || defined(__MINGW32__))
55 #   include <netdb.h>
56 #   include <sys/socket.h>
57 #   include <netinet/in.h>
58 #   include <arpa/inet.h>
59 #endif
60 #include <plib/netSocket.h>
61 #include <plib/sg.h>
62
63 #include <simgear/math/sg_geodesy.hxx>
64 #include <simgear/props/props.hxx>
65 #include <simgear/scene/model/modellib.hxx>
66 #include <simgear/scene/model/placementtrans.hxx>
67
68 #include <AIModel/AIBase.hxx>
69 #include <AIModel/AIManager.hxx>
70 #include <AIModel/AIMultiplayer.hxx>
71 #include <Main/globals.hxx>
72 //#include <Scenery/scenery.hxx>
73
74 // These constants are provided so that the ident command can list file versions.
75 const char sMPPLAYER_BID[] = "$Id$";
76 const char sMPPLAYER_HID[] = MPPLAYER_HID;
77
78 //////////////////////////////////////////////////////////////////////
79 //
80 //  constructor
81 //
82 //////////////////////////////////////////////////////////////////////
83 MPPlayer::MPPlayer()
84 {
85     m_Initialised   = false;
86     m_LastUpdate    = 0;
87     m_LastUpdate    = 0;
88     m_LastTime      = 0;
89     m_LastUTime     = 0;
90     m_Elapsed       = 0;
91     m_TimeOffset    = 0.0;
92     m_Callsign      = "none";
93     m_PlayerAddress.set("localhost", 0);
94     m_AIModel = NULL;
95 } // MPPlayer::MPPlayer()
96 //////////////////////////////////////////////////////////////////////
97
98 /******************************************************************
99 * Name: ~MPPlayer
100 * Description: Destructor.
101 ******************************************************************/
102 MPPlayer::~MPPlayer() 
103 {
104     Close();
105 }
106
107 /******************************************************************
108 * Name: Open
109 * Description: Initialises class.
110 ******************************************************************/
111 bool 
112 MPPlayer::Open
113     (
114     const string &Address,
115     const int &Port,
116     const string &Callsign,
117     const string &ModelName,
118     bool LocalPlayer
119     )
120 {
121     bool Success = true;
122
123     if (!m_Initialised)
124     {
125         m_PlayerAddress.set(Address.c_str(), Port);
126         m_Callsign = Callsign;
127         m_ModelName = ModelName;
128         m_LocalPlayer = LocalPlayer;
129         SG_LOG( SG_NETWORK, SG_ALERT, "Initialising " << m_Callsign
130            << " using '" << m_ModelName << "'" );
131         // If the player is remote then load the model
132         if (!LocalPlayer)
133         {
134              try {
135                  LoadAI();
136              } catch (...) {
137                  SG_LOG( SG_NETWORK, SG_ALERT,
138                    "Failed to load remote model '" << ModelName << "'." );
139                  return false;
140              }
141         }
142         m_Initialised = Success;
143     }
144     else
145     {
146         SG_LOG( SG_NETWORK, SG_ALERT, "MPPlayer::Open - "
147           << "Attempt to open an already opened player connection." );
148         Success = false;
149     }
150     /* Return true if open succeeds */
151     return Success;
152 }
153
154 /******************************************************************
155 * Name: Close
156 * Description: Resets the object.
157 ******************************************************************/
158 void
159 MPPlayer::Close(void)
160 {
161     // Remove the model from the game
162     if (m_Initialised && !m_LocalPlayer) 
163     {
164         m_Initialised = 0;
165         
166         // get the model manager
167         FGAIManager *aiModelMgr = (FGAIManager *) globals->get_subsystem("ai_model");
168         if (!aiModelMgr) {
169             SG_LOG( SG_NETWORK, SG_ALERT, "MPPlayer::Close - "
170                     << "Cannot find AI model manager!" );
171             return;
172         }
173         
174         // remove it
175         aiModelMgr->destroyObject(m_AIModel->getID());
176         m_AIModel = NULL;
177     }
178     m_Initialised   = false;
179     m_Updated       = false;
180     m_LastUpdate    = 0;
181     m_LastUpdate    = 0;
182     m_LastTime      = 0;
183     m_LastUTime     = 0;
184     m_Elapsed       = 0;
185     m_TimeOffset    = 0.0;
186     m_Callsign      = "none";
187 }
188
189 /******************************************************************
190 * Name: CheckTime
191 * Description: Checks if the time is valid for a position update
192 * and perhaps sets the time offset
193 ******************************************************************/
194 bool MPPlayer::CheckTime(int time, int timeusec)
195 {
196     double curOffset;
197     int toff, utoff;
198     
199     SGTimeStamp now;
200     
201     // calculate the offset
202     toff = ((int) now.get_seconds()) - time;
203     utoff = ((int) now.get_usec()) - timeusec;
204     while (utoff < 0) {
205         toff--;
206         utoff += 1000000;
207     }
208     
209     // set it
210     curOffset = ((double)toff) + (((double)utoff) / 1000000);
211         
212     if (m_LastUpdate == 0) {
213         // set the main offset
214         m_TimeOffset = curOffset;
215         m_Elapsed = 0;
216         return true;
217     } else {
218         // check it
219         if (time < m_LastTime ||
220             (time == m_LastTime && timeusec <= m_LastUTime)) {
221             return false;
222         } else {
223             // set the current offset
224             m_LastOffset = curOffset;
225             // calculate the Hz
226             toff = time - m_LastTime;
227             utoff = timeusec - m_LastUTime;
228             while (utoff < 0) {
229                 toff--;
230                 utoff += 1000000;
231             }
232             m_Elapsed = ((double)toff) + (((double)utoff)/1000000);
233             
234             m_LastTime = time;
235             m_LastUTime = timeusec;
236             
237             return true;
238         }
239     }
240 }
241
242 /******************************************************************
243 * Name: SetPosition
244 * Description: Updates position data held for this player and resets
245 * the last update time.
246 ******************************************************************/
247 void
248 MPPlayer::SetPosition
249     (
250         const double lat, const double lon, const double alt,
251         const double heading, const double roll, const double pitch,
252         const double speedN, const double speedE, const double speedD,
253         const double left_aileron, const double right_aileron, const double elevator, const double rudder,
254         //const double rpms[6],
255         const double rateH, const double rateR, const double rateP,
256         const double accN, const double accE, const double accD
257     )
258 {
259     // Save the position matrix and update time
260     if (m_Initialised)
261     {
262         // calculate acceleration
263         /*if (m_Elapsed > 0) {
264             m_accN = (speedN - m_speedN) / m_Elapsed;
265             m_accE = (speedE - m_speedE) / m_Elapsed;
266             m_accD = (speedD - m_speedD) / m_Elapsed;
267         } else {
268             m_accN = 0;
269             m_accE = 0;
270             m_accD = 0;
271         }*/
272         
273         // store the position
274         m_lat = lat;
275         m_lon = lon;
276         m_alt = alt;
277         m_hdg = heading;
278         m_roll = roll;
279         m_pitch = pitch;
280         m_speedN = speedN;
281         m_speedE = speedE;
282         m_speedD = speedD;
283         m_accN = accN;
284         m_accE = accE;
285         m_accD = accD;
286         m_left_aileron = left_aileron;
287         m_right_aileron = right_aileron;
288         m_elevator = elevator;
289         m_rudder = rudder;
290
291         /*for (int i = 0; i < 6; i++) {
292             m_rpms[i] = rpms[i];
293         }
294         m_rateH = rateH;
295         m_rateR = rateR;
296         m_rateP = rateP;*/
297         
298         if (!m_LocalPlayer) {
299             m_AIModel->setLatitude(m_lat);
300             m_AIModel->setLongitude(m_lon);
301             m_AIModel->setAltitude(m_alt);
302             m_AIModel->setHeading(m_hdg);
303             m_AIModel->setBank(m_roll);
304             m_AIModel->setPitch(m_pitch);
305             m_AIModel->setSpeedN(m_speedN);
306             m_AIModel->setSpeedE(m_speedE);
307             m_AIModel->setSpeedD(m_speedD); 
308             m_AIModel->setAccN(m_accN);
309             m_AIModel->setAccE(m_accE);
310             m_AIModel->setAccD(m_accD);
311             m_AIModel->setRateH(m_rateH);
312             m_AIModel->setRateR(m_rateR);
313             m_AIModel->setRateP(m_rateP);
314                 
315             // set properties
316             SGPropertyNode *root = m_AIModel->getProps();
317             root->getNode("surface-positions/left-aileron-pos-norm", true)->setDoubleValue(m_left_aileron);
318             root->getNode("surface-positions/right-aileron-pos-norm", true)->setDoubleValue(m_right_aileron);
319             root->getNode("surface-positions/elevator-pos-norm", true)->setDoubleValue(m_elevator);
320             root->getNode("surface-positions/rudder-pos-norm", true)->setDoubleValue(m_rudder);
321             /*root->getNode("engines/engine/rpm", true)->setDoubleValue(m_rpms[0]);
322             root->getNode("engines/engine[1]/rpm", true)->setDoubleValue(m_rpms[1]);
323             root->getNode("engines/engine[2]/rpm", true)->setDoubleValue(m_rpms[2]);
324             root->getNode("engines/engine[3]/rpm", true)->setDoubleValue(m_rpms[3]);
325             root->getNode("engines/engine[4]/rpm", true)->setDoubleValue(m_rpms[4]);
326             root->getNode("engines/engine[5]/rpm", true)->setDoubleValue(m_rpms[5]);*/
327                 
328             // Adjust by the last offset
329             //cout << "OFFSET: " << (m_LastOffset - m_TimeOffset) << endl;
330             
331             //m_AIModel->timewarp(m_LastOffset - m_TimeOffset);
332
333             // set the timestamp for the data update (sim elapsed time (secs))
334             m_AIModel->setTimeStamp();
335         }
336         
337         time(&m_LastUpdate);
338         
339         m_Updated = true;
340     }
341 }
342
343 /******************************************************************
344  * Name: SetProperty
345  * Description: Sets a property of this player.
346  ******************************************************************/
347 void MPPlayer::SetProperty(string property, SGPropertyNode::Type type, double val)
348 {    
349     // get rid of any leading /
350     while (property[0] == '/') property = property.substr(1);
351     
352     // get our root node
353     SGPropertyNode *node = m_AIModel->getProps()->getNode(property.c_str(), true);
354         
355     // set the property
356     switch (type) {
357         case 2:
358             node->setBoolValue( val != 0.0 );
359             break;
360         case 3:
361             node->setIntValue((int) val);
362             break;
363         case 4:
364             node->setLongValue((long) val);
365             break;
366         case 5:
367             node->setFloatValue((float) val);
368             break;
369         case 6:
370         default:
371             node->setDoubleValue(val);
372     }
373 }
374
375 /******************************************************************
376 * Name: Draw
377 * Description: Updates the position for the player's model
378 * The state of the player's data is returned.
379 ******************************************************************/
380 MPPlayer::TPlayerDataState
381 MPPlayer::Draw (void)
382 {
383     MPPlayer::TPlayerDataState eResult = PLAYER_DATA_NOT_AVAILABLE;
384     if (m_Initialised && !m_LocalPlayer) 
385     {
386         if ((time(NULL) - m_LastUpdate < TIME_TO_LIVE))
387         {
388             // Peform an update if it has changed since the last update
389             if (m_Updated)
390             {
391                 /*
392                 m_AIModel->setLatitude(m_lat);
393                 m_AIModel->setLongitude(m_lon);
394                 m_AIModel->setAltitude(m_alt);
395                 m_AIModel->setHeading(m_hdg);
396                 m_AIModel->setBank(m_roll);
397                 m_AIModel->setPitch(m_pitch);
398                 m_AIModel->setSpeedN(m_speedN);
399                 m_AIModel->setSpeedE(m_speedE);
400                 m_AIModel->_setVS_fps(m_speedU*60.0); // it needs input in fpm
401                 m_AIModel->setRateH(m_rateH);
402                 m_AIModel->setRateR(m_rateR);
403                 m_AIModel->setRateP(m_rateP);
404                 
405                 // set properties
406                 SGPropertyNode *root = m_AIModel->getProps();
407                 root->getNode("controls/flight/aileron", true)->setDoubleValue(m_aileron);
408                 root->getNode("controls/flight/elevator", true)->setDoubleValue(m_elevator);
409                 root->getNode("controls/flight/rudder", true)->setDoubleValue(m_rudder);
410                 root->getNode("engines/engine/rpm", true)->setDoubleValue(m_rpms[0]);
411                 root->getNode("engines/engine[1]/rpm", true)->setDoubleValue(m_rpms[1]);
412                 root->getNode("engines/engine[2]/rpm", true)->setDoubleValue(m_rpms[2]);
413                 root->getNode("engines/engine[3]/rpm", true)->setDoubleValue(m_rpms[3]);
414                 root->getNode("engines/engine[4]/rpm", true)->setDoubleValue(m_rpms[4]);
415                 root->getNode("engines/engine[5]/rpm", true)->setDoubleValue(m_rpms[5]);
416                 
417                 // Adjust by the last offset
418                 m_AIModel->update(m_LastOffset - m_TimeOffset);
419                 */
420                 eResult = PLAYER_DATA_AVAILABLE;
421                 
422                 // Clear the updated flag so that the position data
423                 // is only available if it has changed
424                 m_Updated = false;
425             }
426         }
427         else
428         {   // Data has not been updated for some time.
429             eResult = PLAYER_DATA_EXPIRED;
430         }
431     }
432     return eResult;
433 }
434
435 /******************************************************************
436 * Name: Callsign
437 * Description: Returns the player's callsign.
438 ******************************************************************/
439 string
440 MPPlayer::Callsign(void) const
441 {
442     return m_Callsign;
443 }
444
445 /******************************************************************
446 * Name: CompareCallsign
447 * Description: Returns true if the player's callsign matches
448 * the given callsign.
449 ******************************************************************/
450 bool
451 MPPlayer::CompareCallsign(const char *Callsign) const 
452 {
453     return (m_Callsign == Callsign);
454 }
455
456 /******************************************************************
457  * Name: LoadAI
458  * Description: Loads the AI model into the AI core.
459 ******************************************************************/
460 void
461 MPPlayer::LoadAI(void)
462 {
463     // then get the model manager
464     FGAIManager *aiModelMgr = (FGAIManager *) globals->get_subsystem("ai_model");
465     if (!aiModelMgr) {
466         SG_LOG( SG_NETWORK, SG_ALERT, "MPPlayer::LoadAI - "
467                 << "Cannot find AI model manager!" );
468         return;
469     }
470     
471     // then get the model
472     fgSetBool("/sim/freeze/clock", true);
473     m_AIModel = new FGAIMultiplayer;
474     m_AIModel->setAcType("Multiplayer");
475     m_AIModel->setCompany(m_Callsign);
476     m_AIModel->setPath(m_ModelName.c_str());
477     aiModelMgr->attach(m_AIModel);
478     fgSetBool("/sim/freeze/clock", false);
479 }
480
481 /******************************************************************
482 * Name: FillPosMsg
483 * Description: Populates the header and data for a position message.
484 ******************************************************************/
485 void
486 MPPlayer::FillPosMsg
487     (
488     T_MsgHdr *MsgHdr,
489     T_PositionMsg *PosMsg
490     )
491 {
492     SGTimeStamp now;
493
494     FillMsgHdr(MsgHdr, POS_DATA_ID);
495     strncpy(PosMsg->Model, m_ModelName.c_str(), MAX_MODEL_NAME_LEN);
496     PosMsg->Model[MAX_MODEL_NAME_LEN - 1] = '\0';
497     PosMsg->time = XDR_encode_uint32 (now.get_seconds());
498     PosMsg->timeusec = XDR_encode_uint32 (now.get_usec());
499     PosMsg->lat = XDR_encode_double (m_lat);
500     PosMsg->lon = XDR_encode_double (m_lon);
501     PosMsg->alt = XDR_encode_double (m_alt);
502     PosMsg->hdg = XDR_encode_double (m_hdg);
503     PosMsg->roll = XDR_encode_double (m_roll);
504     PosMsg->pitch = XDR_encode_double (m_pitch);
505     PosMsg->speedN = XDR_encode_double (m_speedN);
506     PosMsg->speedE = XDR_encode_double (m_speedE);
507     PosMsg->speedD = XDR_encode_double (m_speedD);
508     PosMsg->left_aileron = XDR_encode_float ((float) m_left_aileron);
509     PosMsg->right_aileron = XDR_encode_float ((float) m_right_aileron);
510     PosMsg->elevator = XDR_encode_float ((float) m_elevator);
511     PosMsg->rudder = XDR_encode_float ((float) m_rudder);
512     /*for (int i = 0; i < 6; i++) {
513         PosMsg->rpms[i] = XDR_encode_float ((float) m_rpms[i]);
514     }*/
515     PosMsg->rateH =  XDR_encode_float ((float) m_rateH);
516     PosMsg->rateR =  XDR_encode_float ((float) m_rateR);
517     PosMsg->rateP =  XDR_encode_float ((float) m_rateP);
518     PosMsg->accN  =  XDR_encode_float ((float) m_accN);
519     PosMsg->accE =  XDR_encode_float ((float) m_accE);
520     PosMsg->accD =  XDR_encode_float ((float) m_accD);
521 }
522
523 /******************************************************************
524 * Name: FillMsgHdr
525 * Description: Populates the header of a multiplayer message.
526 ******************************************************************/
527 void
528 MPPlayer::FillMsgHdr
529     (
530     T_MsgHdr *MsgHdr, 
531     const int MsgId
532     )
533 {
534     uint32_t        len;
535
536     switch (MsgId)
537     {
538         case CHAT_MSG_ID:
539             len = sizeof(T_MsgHdr) + sizeof(T_ChatMsg);
540             break;
541         case POS_DATA_ID:
542             len = sizeof(T_MsgHdr) + sizeof(T_PositionMsg);
543             break;
544         case PROP_MSG_ID:
545             len = sizeof(T_MsgHdr) + sizeof(T_PropertyMsg);
546             break;
547         default:
548             len = sizeof(T_MsgHdr);
549             break;
550     }
551     MsgHdr->Magic           = XDR_encode_uint32 (MSG_MAGIC);
552     MsgHdr->Version         = XDR_encode_uint32 (PROTO_VER);
553     MsgHdr->MsgId           = XDR_encode_uint32 (MsgId);
554     MsgHdr->MsgLen          = XDR_encode_uint32 (len);
555     // inet_addr returns address in network byte order
556     // no need to encode it
557     MsgHdr->ReplyAddress   = inet_addr( m_PlayerAddress.getHost() );
558     MsgHdr->ReplyPort      = XDR_encode_uint32 (m_PlayerAddress.getPort());
559     strncpy(MsgHdr->Callsign, m_Callsign.c_str(), MAX_CALLSIGN_LEN);
560     MsgHdr->Callsign[MAX_CALLSIGN_LEN - 1] = '\0';
561 }
562
563 #endif // FG_MPLAYER_AS
564