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"
52 #if !(defined(_MSC_VER) || defined(__MINGW32__))
54 # include <sys/socket.h>
55 # include <netinet/in.h>
56 # include <arpa/inet.h>
58 #include <plib/netSocket.h>
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>
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>
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;
76 //////////////////////////////////////////////////////////////////////
80 //////////////////////////////////////////////////////////////////////
83 m_Initialised = false;
91 m_PlayerAddress.set("localhost", 0);
93 } // MPPlayer::MPPlayer()
94 //////////////////////////////////////////////////////////////////////
96 /******************************************************************
98 * Description: Destructor.
99 ******************************************************************/
100 MPPlayer::~MPPlayer()
105 /******************************************************************
107 * Description: Initialises class.
108 ******************************************************************/
112 const string &Address,
114 const string &Callsign,
115 const string &ModelName,
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
135 SG_LOG( SG_NETWORK, SG_ALERT,
136 "Failed to load remote model '" << ModelName << "'." );
140 m_Initialised = Success;
144 SG_LOG( SG_NETWORK, SG_ALERT, "MPPlayer::Open - "
145 << "Attempt to open an already opened player connection." );
148 /* Return true if open succeeds */
152 /******************************************************************
154 * Description: Resets the object.
155 ******************************************************************/
157 MPPlayer::Close(void)
159 // Remove the model from the game
160 if (m_Initialised && !m_LocalPlayer)
164 // get the model manager
165 FGAIManager *aiModelMgr = (FGAIManager *) globals->get_subsystem("ai_model");
167 SG_LOG( SG_NETWORK, SG_ALERT, "MPPlayer::Close - "
168 << "Cannot find AI model manager!" );
173 aiModelMgr->destroyObject(m_AIModel->getID());
176 m_Initialised = false;
187 /******************************************************************
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)
199 gettimeofday(&tv, NULL);
201 // calculate the offset
202 toff = ((int) tv.tv_sec) - time;
203 utoff = ((int) tv.tv_usec) - timeusec;
210 curOffset = ((double)toff) + (((double)utoff) / 1000000);
212 if (m_LastUpdate == 0) {
213 // set the main offset
214 m_TimeOffset = curOffset;
219 if (time < m_LastTime ||
220 (time == m_LastTime && timeusec <= m_LastUTime)) {
223 // set the current offset
224 m_LastOffset = curOffset;
226 toff = time - m_LastTime;
227 utoff = timeusec - m_LastUTime;
232 m_Elapsed = ((double)toff) + (((double)utoff)/1000000);
235 m_LastUTime = timeusec;
242 /******************************************************************
244 * Description: Updates position data held for this player and resets
245 * the last update time.
246 ******************************************************************/
248 MPPlayer::SetPosition
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
261 // Save the position matrix and update time
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;
275 // store the position
288 m_left_aileron = left_aileron;
289 m_right_aileron = right_aileron;
290 m_elevator = elevator;
293 /*for (int i = 0; i < 6; i++) {
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);
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]);*/
330 // Adjust by the last offset
331 //cout << "OFFSET: " << (m_LastOffset - m_TimeOffset) << endl;
333 //m_AIModel->timewarp(m_LastOffset - m_TimeOffset);
335 // set the timestamp for the data update (sim elapsed time (secs))
336 m_AIModel->setTimeStamp();
345 /******************************************************************
347 * Description: Sets a property of this player.
348 ******************************************************************/
349 void MPPlayer::SetProperty(string property, SGPropertyNode::Type type, double val)
351 // get rid of any leading /
352 while (property[0] == '/') property = property.substr(1);
355 SGPropertyNode *node = m_AIModel->getProps()->getNode(property.c_str(), true);
360 node->setBoolValue((bool) val);
363 node->setIntValue((int) val);
366 node->setLongValue((long) val);
369 node->setFloatValue((float) val);
373 node->setDoubleValue(val);
377 /******************************************************************
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)
385 MPPlayer::TPlayerDataState eResult = PLAYER_DATA_NOT_AVAILABLE;
386 if (m_Initialised && !m_LocalPlayer)
388 if ((time(NULL) - m_LastUpdate < TIME_TO_LIVE))
390 // Peform an update if it has changed since the last update
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);
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]);
419 // Adjust by the last offset
420 m_AIModel->update(m_LastOffset - m_TimeOffset);
422 eResult = PLAYER_DATA_AVAILABLE;
424 // Clear the updated flag so that the position data
425 // is only available if it has changed
430 { // Data has not been updated for some time.
431 eResult = PLAYER_DATA_EXPIRED;
437 /******************************************************************
439 * Description: Returns the player's callsign.
440 ******************************************************************/
442 MPPlayer::Callsign(void) const
447 /******************************************************************
448 * Name: CompareCallsign
449 * Description: Returns true if the player's callsign matches
450 * the given callsign.
451 ******************************************************************/
453 MPPlayer::CompareCallsign(const char *Callsign) const
455 return (m_Callsign == Callsign);
458 /******************************************************************
460 * Description: Loads the AI model into the AI core.
461 ******************************************************************/
463 MPPlayer::LoadAI(void)
465 // then get the model manager
466 FGAIManager *aiModelMgr = (FGAIManager *) globals->get_subsystem("ai_model");
468 SG_LOG( SG_NETWORK, SG_ALERT, "MPPlayer::LoadAI - "
469 << "Cannot find AI model manager!" );
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);
483 /******************************************************************
485 * Description: Populates the header and data for a position message.
486 ******************************************************************/
491 T_PositionMsg *PosMsg
495 gettimeofday(&tv, NULL);
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]);
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);
526 /******************************************************************
528 * Description: Populates the header of a multiplayer message.
529 ******************************************************************/
542 len = sizeof(T_MsgHdr) + sizeof(T_ChatMsg);
545 len = sizeof(T_MsgHdr) + sizeof(T_PositionMsg);
548 len = sizeof(T_MsgHdr) + sizeof(T_PropertyMsg);
551 len = sizeof(T_MsgHdr);
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';
566 #endif // FG_MPLAYER_AS