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 <simgear/timing/timestamp.hxx>
51 #include "mpplayer.hxx"
54 #if !(defined(_MSC_VER) || defined(__MINGW32__))
56 # include <sys/socket.h>
57 # include <netinet/in.h>
58 # include <arpa/inet.h>
60 #include <plib/netSocket.h>
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>
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>
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;
78 //////////////////////////////////////////////////////////////////////
82 //////////////////////////////////////////////////////////////////////
85 m_Initialised = false;
93 m_PlayerAddress.set("localhost", 0);
95 } // MPPlayer::MPPlayer()
96 //////////////////////////////////////////////////////////////////////
98 /******************************************************************
100 * Description: Destructor.
101 ******************************************************************/
102 MPPlayer::~MPPlayer()
107 /******************************************************************
109 * Description: Initialises class.
110 ******************************************************************/
114 const string &Address,
116 const string &Callsign,
117 const string &ModelName,
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
137 SG_LOG( SG_NETWORK, SG_ALERT,
138 "Failed to load remote model '" << ModelName << "'." );
142 m_Initialised = Success;
146 SG_LOG( SG_NETWORK, SG_ALERT, "MPPlayer::Open - "
147 << "Attempt to open an already opened player connection." );
150 /* Return true if open succeeds */
154 /******************************************************************
156 * Description: Resets the object.
157 ******************************************************************/
159 MPPlayer::Close(void)
161 // Remove the model from the game
162 if (m_Initialised && !m_LocalPlayer)
166 // get the model manager
167 FGAIManager *aiModelMgr = (FGAIManager *) globals->get_subsystem("ai_model");
169 SG_LOG( SG_NETWORK, SG_ALERT, "MPPlayer::Close - "
170 << "Cannot find AI model manager!" );
175 aiModelMgr->destroyObject(m_AIModel->getID());
178 m_Initialised = false;
189 /******************************************************************
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)
201 // calculate the offset
202 toff = ((int) now.get_seconds()) - time;
203 utoff = ((int) now.get_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
259 // Save the position matrix and update time
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;
273 // store the position
286 m_left_aileron = left_aileron;
287 m_right_aileron = right_aileron;
288 m_elevator = elevator;
291 /*for (int i = 0; i < 6; i++) {
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);
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]);*/
328 // Adjust by the last offset
329 //cout << "OFFSET: " << (m_LastOffset - m_TimeOffset) << endl;
331 //m_AIModel->timewarp(m_LastOffset - m_TimeOffset);
333 // set the timestamp for the data update (sim elapsed time (secs))
334 m_AIModel->setTimeStamp();
343 /******************************************************************
345 * Description: Sets a property of this player.
346 ******************************************************************/
347 void MPPlayer::SetProperty(string property, SGPropertyNode::Type type, double val)
349 // get rid of any leading /
350 while (property[0] == '/') property = property.substr(1);
353 SGPropertyNode *node = m_AIModel->getProps()->getNode(property.c_str(), true);
358 node->setBoolValue( val != 0.0 );
361 node->setIntValue((int) val);
364 node->setLongValue((long) val);
367 node->setFloatValue((float) val);
371 node->setDoubleValue(val);
375 /******************************************************************
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)
383 MPPlayer::TPlayerDataState eResult = PLAYER_DATA_NOT_AVAILABLE;
384 if (m_Initialised && !m_LocalPlayer)
386 if ((time(NULL) - m_LastUpdate < TIME_TO_LIVE))
388 // Peform an update if it has changed since the last update
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);
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]);
417 // Adjust by the last offset
418 m_AIModel->update(m_LastOffset - m_TimeOffset);
420 eResult = PLAYER_DATA_AVAILABLE;
422 // Clear the updated flag so that the position data
423 // is only available if it has changed
428 { // Data has not been updated for some time.
429 eResult = PLAYER_DATA_EXPIRED;
435 /******************************************************************
437 * Description: Returns the player's callsign.
438 ******************************************************************/
440 MPPlayer::Callsign(void) const
445 /******************************************************************
446 * Name: CompareCallsign
447 * Description: Returns true if the player's callsign matches
448 * the given callsign.
449 ******************************************************************/
451 MPPlayer::CompareCallsign(const char *Callsign) const
453 return (m_Callsign == Callsign);
456 /******************************************************************
458 * Description: Loads the AI model into the AI core.
459 ******************************************************************/
461 MPPlayer::LoadAI(void)
463 // then get the model manager
464 FGAIManager *aiModelMgr = (FGAIManager *) globals->get_subsystem("ai_model");
466 SG_LOG( SG_NETWORK, SG_ALERT, "MPPlayer::LoadAI - "
467 << "Cannot find AI model manager!" );
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);
481 /******************************************************************
483 * Description: Populates the header and data for a position message.
484 ******************************************************************/
489 T_PositionMsg *PosMsg
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]);
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);
523 /******************************************************************
525 * Description: Populates the header of a multiplayer message.
526 ******************************************************************/
539 len = sizeof(T_MsgHdr) + sizeof(T_ChatMsg);
542 len = sizeof(T_MsgHdr) + sizeof(T_PositionMsg);
545 len = sizeof(T_MsgHdr) + sizeof(T_PropertyMsg);
548 len = sizeof(T_MsgHdr);
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';
563 #endif // FG_MPLAYER_AS