// mpplayer.cxx -- routines for a player within a multiplayer Flightgear // // Written by Duncan McCreanor, started February 2003. // duncan.mccreanor@airservicesaustralia.com // // Copyright (C) 2003 Airservices Australia // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as // published by the Free Software Foundation; either version 2 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifdef HAVE_CONFIG_H #include #endif #ifdef FG_MPLAYER_AS /****************************************************************** * $Id$ * * Description: Provides a container for a player in a multiplayer * game. The players network address, model, callsign and positoin * are held. When the player is created and open called, the player's * model is loaded onto the scene. The position transform matrix * is updated by calling SetPosition. When Draw is called the * elapsed time since the last update is checked. If the model * position information has been updated in the last TIME_TO_LIVE * seconds then the model position is updated on the scene. * ******************************************************************/ #include "mpplayer.hxx" #include #if !(defined(_MSC_VER) || defined(__MINGW32__)) # include # include # include # include #endif #include #include #include
#include #include // These constants are provided so that the ident command can list file versions. const char sMPPLAYER_BID[] = "$Id$"; const char sMPPLAYER_HID[] = MPPLAYER_HID; /****************************************************************** * Name: MPPlayer * Description: Constructor. ******************************************************************/ MPPlayer::MPPlayer() { // Initialise private members m_bInitialised = false; m_LastUpdate = 0; m_PlayerAddress.set("localhost", 0); m_sCallsign = "none"; } /****************************************************************** * Name: ~MPPlayer * Description: Destructor. ******************************************************************/ MPPlayer::~MPPlayer() { Close(); } /****************************************************************** * Name: Open * Description: Initialises class. ******************************************************************/ bool MPPlayer::Open(const string &sAddress, const int &iPort, const string &sCallsign, const string &sModelName, bool bLocalPlayer) { bool bSuccess = true; if (!m_bInitialised) { m_PlayerAddress.set(sAddress.c_str(), iPort); m_sCallsign = sCallsign; m_sModelName = sModelName; m_bLocalPlayer = bLocalPlayer; // If the player is remote then load the model if (!bLocalPlayer) { LoadModel(); } m_bInitialised = bSuccess; } else { SG_LOG( SG_NETWORK, SG_ALERT, "MPPlayer::Open - Attempt to open an already open player connection." ); bSuccess = false; } /* Return true if open succeeds */ return bSuccess; } /****************************************************************** * Name: Close * Description: Resets the object. ******************************************************************/ void MPPlayer::Close(void) { // Remove the model from the game if (m_bInitialised && !m_bLocalPlayer) { // Disconnect the model from the transform, then the transform from the scene. m_ModelTrans->removeKid(m_Model); globals->get_scenery()->get_aircraft_branch()->removeKid( m_ModelTrans); // Flush the model loader so that it erases the model from its list of // models. globals->get_model_loader()->flush(); // Assume that plib/ssg deletes the model and transform as their // refcounts should be zero. } m_bInitialised = false; m_bUpdated = false; m_LastUpdate = 0; m_sCallsign = "none"; } /****************************************************************** * Name: SetPosition * Description: Updates position data held for this player and resets * the last update time. ******************************************************************/ void MPPlayer::SetPosition(const sgMat4 PlayerPosMat4) { // Save the position matrix and update time if (m_bInitialised) { sgCopyMat4(m_ModelPos, PlayerPosMat4); time(&m_LastUpdate); m_bUpdated = true; } } /****************************************************************** * Name: Draw * Description: Updates the position for the player's model * The state of the player's data is returned. ******************************************************************/ MPPlayer::TPlayerDataState MPPlayer::Draw(void) { MPPlayer::TPlayerDataState eResult = PLAYER_DATA_NOT_AVAILABLE; sgCoord sgPlayerCoord; if (m_bInitialised && !m_bLocalPlayer) { if ((time(NULL) - m_LastUpdate < TIME_TO_LIVE)) { // Peform an update if it has changed since the last update if (m_bUpdated) { // Transform and update player model sgSetCoord( &sgPlayerCoord, m_ModelPos); m_ModelTrans->setTransform( &sgPlayerCoord ); eResult = PLAYER_DATA_AVAILABLE; // Clear the updated flag so that the position data // is only available if it has changed m_bUpdated = false; } // Data has not been updated for some time. } else { eResult = PLAYER_DATA_EXPIRED; } } return eResult; } /****************************************************************** * Name: Callsign * Description: Returns the player's callsign. ******************************************************************/ string MPPlayer::Callsign(void) const { return m_sCallsign; } /****************************************************************** * Name: CompareCallsign * Description: Returns true if the player's callsign matches * the given callsign. ******************************************************************/ bool MPPlayer::CompareCallsign(const char *sCallsign) const { return (m_sCallsign == sCallsign); } /****************************************************************** * Name: LoadModel * Description: Loads the player's aircraft model. ******************************************************************/ void MPPlayer::LoadModel(void) { m_ModelTrans = new ssgTransform; // Load the model m_Model = globals->get_model_loader()->load_model(m_sModelName); m_Model->clrTraversalMaskBits( SSGTRAV_HOT ); // Add model to transform m_ModelTrans->addKid( m_Model ); // Optimise model and transform ssgFlatten( m_Model ); ssgStripify( m_ModelTrans ); // Place on scene under aircraft branch globals->get_scenery()->get_aircraft_branch()->addKid( m_ModelTrans ); } /****************************************************************** * Name: FillPosMsg * Description: Populates the header and data for a position message. ******************************************************************/ void MPPlayer::FillPosMsg(T_MsgHdr *MsgHdr, T_PositionMsg *PosMsg) { FillMsgHdr(MsgHdr, POS_DATA_ID); strncpy(PosMsg->sModel, m_sModelName.c_str(), MAX_MODEL_NAME_LEN); PosMsg->sModel[MAX_MODEL_NAME_LEN - 1] = '\0'; sgCopyMat4(PosMsg->PlayerPos, m_ModelPos); } /****************************************************************** * Name: FillMsgHdr * Description: Populates the header of a multiplayer message. ******************************************************************/ void MPPlayer::FillMsgHdr(T_MsgHdr *MsgHdr, const int iMsgId) { struct in_addr address; MsgHdr->MsgId = iMsgId; switch (iMsgId) { case CHAT_MSG_ID: MsgHdr->iMsgLen = sizeof(T_MsgHdr) + sizeof(T_ChatMsg); break; case POS_DATA_ID: MsgHdr->iMsgLen = sizeof(T_MsgHdr) + sizeof(T_PositionMsg); break; default: MsgHdr->iMsgLen = sizeof(T_MsgHdr); break; } address.s_addr = inet_addr( m_PlayerAddress.getHost() ); MsgHdr->lReplyAddress = address.s_addr; MsgHdr->iReplyPort = m_PlayerAddress.getPort(); strncpy(MsgHdr->sCallsign, m_sCallsign.c_str(), MAX_CALLSIGN_LEN); MsgHdr->sCallsign[MAX_CALLSIGN_LEN - 1] = '\0'; } #endif // FG_MPLAYER_AS