1 //////////////////////////////////////////////////////////////////////
3 // mpplayer.cxx -- routines for a player within a multiplayer Flightgear
5 // Written by Duncan McCreanor, started February 2003.
6 // duncan.mccreanor@airservicesaustralia.com
8 // With additions by Vivian Meazza, January 2006
10 // Copyright (C) 2003 Airservices Australia
11 // Copyright (C) 2005 Oliver Schroeder
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.
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.
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.
27 //////////////////////////////////////////////////////////////////////
35 /******************************************************************
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.
47 ******************************************************************/
49 #include "mpplayer.hxx"
53 #if !(defined(_MSC_VER) || defined(__MINGW32__))
55 # include <sys/socket.h>
56 # include <netinet/in.h>
57 # include <arpa/inet.h>
59 #include <plib/netSocket.h>
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>
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>
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;
77 //////////////////////////////////////////////////////////////////////
81 //////////////////////////////////////////////////////////////////////
84 m_Initialised = false;
92 m_PlayerAddress.set("localhost", 0);
94 } // MPPlayer::MPPlayer()
95 //////////////////////////////////////////////////////////////////////
97 /******************************************************************
99 * Description: Destructor.
100 ******************************************************************/
101 MPPlayer::~MPPlayer()
106 /******************************************************************
108 * Description: Initialises class.
109 ******************************************************************/
113 const string &Address,
115 const string &Callsign,
116 const string &ModelName,
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
136 SG_LOG( SG_NETWORK, SG_ALERT,
137 "Failed to load remote model '" << ModelName << "'." );
141 m_Initialised = Success;
145 SG_LOG( SG_NETWORK, SG_ALERT, "MPPlayer::Open - "
146 << "Attempt to open an already opened player connection." );
149 /* Return true if open succeeds */
153 /******************************************************************
155 * Description: Resets the object.
156 ******************************************************************/
158 MPPlayer::Close(void)
160 // Remove the model from the game
161 if (m_Initialised && !m_LocalPlayer)
165 // get the model manager
166 FGAIManager *aiModelMgr = (FGAIManager *) globals->get_subsystem("ai_model");
168 SG_LOG( SG_NETWORK, SG_ALERT, "MPPlayer::Close - "
169 << "Cannot find AI model manager!" );
174 aiModelMgr->destroyObject(m_AIModel->getID());
177 m_Initialised = false;
188 /******************************************************************
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)
200 gettimeofday(&tv, NULL);
202 // calculate the offset
203 toff = ((int) tv.tv_sec) - time;
204 utoff = ((int) tv.tv_usec) - timeusec;
211 curOffset = ((double)toff) + (((double)utoff) / 1000000);
213 if (m_LastUpdate == 0) {
214 // set the main offset
215 m_TimeOffset = curOffset;
220 if (time < m_LastTime ||
221 (time == m_LastTime && timeusec <= m_LastUTime)) {
224 // set the current offset
225 m_LastOffset = curOffset;
227 toff = time - m_LastTime;
228 utoff = timeusec - m_LastUTime;
233 m_Elapsed = ((double)toff) + (((double)utoff)/1000000);
236 m_LastUTime = timeusec;
243 /******************************************************************
245 * Description: Updates position data held for this player and resets
246 * the last update time.
247 ******************************************************************/
249 MPPlayer::SetPosition
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
262 // Save the position matrix and update time
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;
276 // store the position
289 m_left_aileron = left_aileron;
290 m_right_aileron = right_aileron;
291 m_elevator = elevator;
294 /*for (int i = 0; i < 6; i++) {
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);
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]);*/
331 // Adjust by the last offset
332 //cout << "OFFSET: " << (m_LastOffset - m_TimeOffset) << endl;
334 //m_AIModel->timewarp(m_LastOffset - m_TimeOffset);
336 // set the timestamp for the data update (sim elapsed time (secs))
337 m_AIModel->setTimeStamp();
346 /******************************************************************
348 * Description: Sets a property of this player.
349 ******************************************************************/
350 void MPPlayer::SetProperty(string property, SGPropertyNode::Type type, double val)
352 // get rid of any leading /
353 while (property[0] == '/') property = property.substr(1);
356 SGPropertyNode *node = m_AIModel->getProps()->getNode(property.c_str(), true);
361 node->setBoolValue((bool) val);
364 node->setIntValue((int) val);
367 node->setLongValue((long) val);
370 node->setFloatValue((float) val);
374 node->setDoubleValue(val);
378 /******************************************************************
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)
386 MPPlayer::TPlayerDataState eResult = PLAYER_DATA_NOT_AVAILABLE;
387 if (m_Initialised && !m_LocalPlayer)
389 if ((time(NULL) - m_LastUpdate < TIME_TO_LIVE))
391 // Peform an update if it has changed since the last update
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);
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]);
420 // Adjust by the last offset
421 m_AIModel->update(m_LastOffset - m_TimeOffset);
423 eResult = PLAYER_DATA_AVAILABLE;
425 // Clear the updated flag so that the position data
426 // is only available if it has changed
431 { // Data has not been updated for some time.
432 eResult = PLAYER_DATA_EXPIRED;
438 /******************************************************************
440 * Description: Returns the player's callsign.
441 ******************************************************************/
443 MPPlayer::Callsign(void) const
448 /******************************************************************
449 * Name: CompareCallsign
450 * Description: Returns true if the player's callsign matches
451 * the given callsign.
452 ******************************************************************/
454 MPPlayer::CompareCallsign(const char *Callsign) const
456 return (m_Callsign == Callsign);
459 /******************************************************************
461 * Description: Loads the AI model into the AI core.
462 ******************************************************************/
464 MPPlayer::LoadAI(void)
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;
473 // then get the model manager
474 FGAIManager *aiModelMgr = (FGAIManager *) globals->get_subsystem("ai_model");
476 SG_LOG( SG_NETWORK, SG_ALERT, "MPPlayer::LoadAI - "
477 << "Cannot find AI model manager!" );
481 // then get the model
482 fgSetBool("/sim/freeze/clock", true);
483 m_AIModel = (FGAIMultiplayer *) aiModelMgr->createMultiplayer(&aiModel);
484 fgSetBool("/sim/freeze/clock", false);
487 /******************************************************************
489 * Description: Populates the header and data for a position message.
490 ******************************************************************/
495 T_PositionMsg *PosMsg
499 gettimeofday(&tv, NULL);
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]);
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);
530 /******************************************************************
532 * Description: Populates the header of a multiplayer message.
533 ******************************************************************/
546 len = sizeof(T_MsgHdr) + sizeof(T_ChatMsg);
549 len = sizeof(T_MsgHdr) + sizeof(T_PositionMsg);
552 len = sizeof(T_MsgHdr) + sizeof(T_PropertyMsg);
555 len = sizeof(T_MsgHdr);
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';
570 #endif // FG_MPLAYER_AS