1
0
Fork 0
flightgear/src/MultiPlayer/mpplayer.cxx

295 lines
8.2 KiB
C++
Raw Normal View History

// 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.
//
/******************************************************************
* $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 <stdlib.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <plib/netSocket.h>
#include <Main/globals.hxx>
#include <Model/loader.hxx>
#include <Scenery/scenery.hxx>
// 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);
}
/******************************************************************
* 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 {
cerr << "MPPlayer::Open - Attempt to open an already open player connection." << endl;
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_bLocalPlayer) {
globals->get_scenery()->get_scene_graph()->removeKid(m_ModelSel);
}
m_bInitialised = false;
m_bUpdated = false;
m_LastUpdate = 0;
}
/******************************************************************
* 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) {
memcpy(m_ModelPos, PlayerPosMat4, sizeof(sgMat4));
time(&m_LastUpdate);
m_bUpdated = true;
}
}
/******************************************************************
* Name: Draw
* Description: Updates the position for the player's model
* The state of the player (old, initialised etc)
* is returned.
******************************************************************/
int MPPlayer::Draw(void) {
int iResult = PLAYER_DATA_NOT_AVAILABLE;
sgCoord sgPlayerCoord;
if (m_bInitialised) {
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
m_ModelSel->select(1);
sgSetCoord( &sgPlayerCoord, m_ModelPos);
m_ModelTrans->setTransform( &sgPlayerCoord );
iResult = 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 {
iResult = PLAYER_DATA_EXPIRED;
}
}
return iResult;
}
/******************************************************************
* 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_ModelSel = new ssgSelector;
m_ModelTrans = new ssgTransform;
ssgEntity *Model = globals->get_model_loader()->load_model(m_sModelName);
Model->clrTraversalMaskBits( SSGTRAV_HOT );
m_ModelTrans->addKid( Model );
m_ModelSel->addKid( m_ModelTrans );
ssgFlatten( Model );
ssgStripify( m_ModelSel );
globals->get_scenery()->get_scene_graph()->addKid( m_ModelSel );
globals->get_scenery()->get_scene_graph()->addKid( Model );
}
/******************************************************************
* 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';
memcpy(PosMsg->PlayerPos, m_ModelPos, sizeof(sgMat4));
}
/******************************************************************
* 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;
}
inet_aton(m_PlayerAddress.getHost(), &address);
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';
}