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