1 // mpplayer.cxx -- routines for a player within a multiplayer Flightgear
3 // Written by Duncan McCreanor, started February 2003.
4 // duncan.mccreanor@airservicesaustralia.com
6 // Copyright (C) 2003 Airservices Australia
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // General Public License for more details.
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 /******************************************************************
32 * Description: Provides a container for a player in a multiplayer
33 * game. The players network address, model, callsign and positoin
34 * are held. When the player is created and open called, the player's
35 * model is loaded onto the scene. The position transform matrix
36 * is updated by calling SetPosition. When Draw is called the
37 * elapsed time since the last update is checked. If the model
38 * position information has been updated in the last TIME_TO_LIVE
39 * seconds then the model position is updated on the scene.
41 ******************************************************************/
43 #include "mpplayer.hxx"
46 #if !(defined(_MSC_VER) || defined(__MINGW32__))
48 # include <sys/socket.h>
49 # include <netinet/in.h>
50 # include <arpa/inet.h>
52 #include <plib/netSocket.h>
55 #include <simgear/scene/model/modellib.hxx>
56 #include <simgear/scene/model/placementtrans.hxx>
58 #include <Main/globals.hxx>
59 #include <Scenery/scenery.hxx>
62 // These constants are provided so that the ident command can list file versions.
63 const char sMPPLAYER_BID[] = "$Id$";
64 const char sMPPLAYER_HID[] = MPPLAYER_HID;
67 /******************************************************************
69 * Description: Constructor.
70 ******************************************************************/
71 MPPlayer::MPPlayer() {
73 // Initialise private members
74 m_bInitialised = false;
76 m_PlayerAddress.set("localhost", 0);
83 /******************************************************************
85 * Description: Destructor.
86 ******************************************************************/
87 MPPlayer::~MPPlayer() {
94 /******************************************************************
96 * Description: Initialises class.
97 ******************************************************************/
98 bool MPPlayer::Open(const string &sAddress, const int &iPort, const string &sCallsign, const string &sModelName, bool bLocalPlayer) {
100 bool bSuccess = true;
102 if (!m_bInitialised) {
104 m_PlayerAddress.set(sAddress.c_str(), iPort);
105 m_sCallsign = sCallsign;
106 m_sModelName = sModelName;
107 m_bLocalPlayer = bLocalPlayer;
109 // If the player is remote then load the model
114 SG_LOG( SG_NETWORK, SG_ALERT, "Failed to load remote model '" << sModelName << "'." );
119 m_bInitialised = bSuccess;
122 SG_LOG( SG_NETWORK, SG_ALERT, "MPPlayer::Open - Attempt to open an already open player connection." );
127 /* Return true if open succeeds */
133 /******************************************************************
135 * Description: Resets the object.
136 ******************************************************************/
137 void MPPlayer::Close(void) {
139 // Remove the model from the game
140 if (m_bInitialised && !m_bLocalPlayer) {
142 // Disconnect the model from the transform, then the transform from the scene.
143 m_ModelTrans->removeKid(m_Model);
144 globals->get_scenery()->unregister_placement_transform(m_ModelTrans);
145 globals->get_scenery()->get_aircraft_branch()->removeKid( m_ModelTrans);
147 // Flush the model loader so that it erases the model from its list of
149 globals->get_model_lib()->flush1();
151 // Assume that plib/ssg deletes the model and transform as their
152 // refcounts should be zero.
156 m_bInitialised = false;
159 m_sCallsign = "none";
164 /******************************************************************
166 * Description: Updates position data held for this player and resets
167 * the last update time.
168 ******************************************************************/
169 void MPPlayer::SetPosition(const sgQuat PlayerOrientation,
170 const sgdVec3 PlayerPosition) {
173 // Save the position matrix and update time
174 if (m_bInitialised) {
175 sgdCopyVec3(m_ModelPosition, PlayerPosition);
176 sgCopyVec4(m_ModelOrientation, PlayerOrientation);
185 /******************************************************************
187 * Description: Updates the position for the player's model
188 * The state of the player's data is returned.
189 ******************************************************************/
190 MPPlayer::TPlayerDataState MPPlayer::Draw(void) {
192 MPPlayer::TPlayerDataState eResult = PLAYER_DATA_NOT_AVAILABLE;
194 if (m_bInitialised && !m_bLocalPlayer) {
195 if ((time(NULL) - m_LastUpdate < TIME_TO_LIVE)) {
196 // Peform an update if it has changed since the last update
199 // Transform and update player model
201 sgMakeIdentMat4(orMat);
202 sgQuatToMatrix(orMat, m_ModelOrientation);
203 m_ModelTrans->setTransform(m_ModelPosition, orMat);
205 eResult = PLAYER_DATA_AVAILABLE;
207 // Clear the updated flag so that the position data
208 // is only available if it has changed
212 // Data has not been updated for some time.
214 eResult = PLAYER_DATA_EXPIRED;
224 /******************************************************************
226 * Description: Returns the player's callsign.
227 ******************************************************************/
228 string MPPlayer::Callsign(void) const {
235 /******************************************************************
236 * Name: CompareCallsign
237 * Description: Returns true if the player's callsign matches
238 * the given callsign.
239 ******************************************************************/
240 bool MPPlayer::CompareCallsign(const char *sCallsign) const {
242 return (m_sCallsign == sCallsign);
247 /******************************************************************
249 * Description: Loads the player's aircraft model.
250 ******************************************************************/
251 void MPPlayer::LoadModel(void) {
254 m_ModelTrans = new ssgPlacementTransform;
257 m_Model = globals->get_model_lib()->load_model( globals->get_fg_root(),
259 globals->get_props(),
260 globals->get_sim_time_sec() );
261 m_Model->clrTraversalMaskBits( SSGTRAV_HOT );
263 // Add model to transform
264 m_ModelTrans->addKid( m_Model );
266 // Optimise model and transform
267 ssgFlatten( m_Model );
268 ssgStripify( m_ModelTrans );
270 // Place on scene under aircraft branch
271 globals->get_scenery()->get_aircraft_branch()->addKid( m_ModelTrans );
272 globals->get_scenery()->register_placement_transform( m_ModelTrans);
278 /******************************************************************
280 * Description: Populates the header and data for a position message.
281 ******************************************************************/
282 void MPPlayer::FillPosMsg(T_MsgHdr *MsgHdr, T_PositionMsg *PosMsg) {
284 FillMsgHdr(MsgHdr, POS_DATA_ID);
286 strncpy(PosMsg->sModel, m_sModelName.c_str(), MAX_MODEL_NAME_LEN);
287 PosMsg->sModel[MAX_MODEL_NAME_LEN - 1] = '\0';
288 sgdCopyVec3(PosMsg->PlayerPosition, m_ModelPosition);
289 sgCopyQuat(PosMsg->PlayerOrientation, m_ModelOrientation);
295 /******************************************************************
297 * Description: Populates the header of a multiplayer message.
298 ******************************************************************/
299 void MPPlayer::FillMsgHdr(T_MsgHdr *MsgHdr, const int iMsgId) {
301 struct in_addr address;
303 MsgHdr->MsgId = iMsgId;
307 MsgHdr->iMsgLen = sizeof(T_MsgHdr) + sizeof(T_ChatMsg);
310 MsgHdr->iMsgLen = sizeof(T_MsgHdr) + sizeof(T_PositionMsg);
313 MsgHdr->iMsgLen = sizeof(T_MsgHdr);
317 address.s_addr = inet_addr( m_PlayerAddress.getHost() );
318 MsgHdr->lReplyAddress = address.s_addr;
320 MsgHdr->iReplyPort = m_PlayerAddress.getPort();
322 strncpy(MsgHdr->sCallsign, m_sCallsign.c_str(), MAX_CALLSIGN_LEN);
323 MsgHdr->sCallsign[MAX_CALLSIGN_LEN - 1] = '\0';
328 #endif // FG_MPLAYER_AS