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