diff --git a/Thanks b/Thanks index da0e6bc6e..e37bacf69 100644 --- a/Thanks +++ b/Thanks @@ -622,6 +622,7 @@ Roy Vegard Ovesen Stuart Buchanan + Substantial additions to the Getting Started Manual Enhancements to the Cessna-310 3d model. Added a generic yoke model, a generic throttle quadrant model and a generic pedal set model. diff --git a/src/AIModel/AIBase.cxx b/src/AIModel/AIBase.cxx index 51a959e40..8ec57b527 100644 --- a/src/AIModel/AIBase.cxx +++ b/src/AIModel/AIBase.cxx @@ -56,7 +56,7 @@ FGAIBase::FGAIBase() _refID( _newAIModelID() ) { _type_str = "model"; - tgt_heading = tgt_altitude = tgt_speed = 0.0; + tgt_heading = hdg = tgt_altitude = tgt_speed = 0.0; tgt_roll = roll = tgt_pitch = tgt_yaw = tgt_vs = vs = pitch = 0.0; bearing = elevation = range = rdot = 0.0; x_shift = y_shift = rotation = 0.0; diff --git a/src/AIModel/AIBase.hxx b/src/AIModel/AIBase.hxx index 02b9e07e0..c64589461 100644 --- a/src/AIModel/AIBase.hxx +++ b/src/AIModel/AIBase.hxx @@ -113,7 +113,7 @@ public: inline const Point3D& GetPos() const { return(pos); } enum object_type { otNull = 0, otAircraft, otShip, otCarrier, otBallistic, - otRocket, otStorm, otThermal, otStatic, + otRocket, otStorm, otThermal, otStatic, otMultiplayer, MAX_OBJECTS }; // Needs to be last!!! virtual bool init(); @@ -127,6 +127,7 @@ public: void setLatitude( double latitude ); void setLongitude( double longitude ); void setBank( double bank ); + void setPitch( double newpitch ); void setRadius ( double radius ); void setXoffset( double x_offset ); void setYoffset( double y_offset ); @@ -273,6 +274,10 @@ inline void FGAIBase::setBank( double bank ) { no_roll = false; } +inline void FGAIBase::setPitch( double newpitch ) { + pitch = tgt_pitch = newpitch; +} + inline void FGAIBase::setLongitude( double longitude ) { pos.setlon( longitude ); } diff --git a/src/AIModel/AIManager.cxx b/src/AIModel/AIManager.cxx index 0047d0199..c1281004e 100644 --- a/src/AIModel/AIManager.cxx +++ b/src/AIModel/AIManager.cxx @@ -37,6 +37,7 @@ #include "AIThermal.hxx" #include "AICarrier.hxx" #include "AIStatic.hxx" +#include "AIMultiplayer.hxx" SG_USING_STD(list); @@ -192,6 +193,23 @@ FGAIManager::createAircraft( FGAIModelEntity *entity, FGAISchedule *ref) { return ai_plane; } +void* +FGAIManager::createMultiplayer( FGAIModelEntity *entity ) { + + FGAIMultiplayer* ai_plane = new FGAIMultiplayer(this); + ai_list.push_back(ai_plane); + ++numObjects[0]; + ++numObjects[FGAIBase::otMultiplayer]; + ai_plane->setAcType(entity->acType); + ai_plane->setCompany(entity->company); + ai_plane->setPath(entity->path.c_str()); + + ai_plane->init(); + ai_plane->bind(); + return ai_plane; +} + + void* FGAIManager::createShip( FGAIModelEntity *entity ) { diff --git a/src/AIModel/AIManager.hxx b/src/AIModel/AIManager.hxx index 1c57df03a..97fccb11d 100644 --- a/src/AIModel/AIManager.hxx +++ b/src/AIModel/AIManager.hxx @@ -85,6 +85,7 @@ public: void* createBallistic( FGAIModelEntity *entity ); void* createAircraft( FGAIModelEntity *entity, FGAISchedule *ref=0 ); + void* createMultiplayer( FGAIModelEntity *entity ); void* createThermal( FGAIModelEntity *entity ); void* createStorm( FGAIModelEntity *entity ); void* createShip( FGAIModelEntity *entity ); diff --git a/src/AIModel/AIMultiplayer.cxx b/src/AIModel/AIMultiplayer.cxx new file mode 100755 index 000000000..33be23f8e --- /dev/null +++ b/src/AIModel/AIMultiplayer.cxx @@ -0,0 +1,278 @@ +// FGAIMultiplayer - FGAIBase-derived class creates an AI multiplayer aircraft +// +// Based on FGAIAircraft +// Written by David Culp, started October 2003. +// Also by Gregor Richards, started December 2005. +// With additions by Vivian Meazza, January 2006 +// +// Copyright (C) 2003 David P. Culp - davidculp2@comcast.net +// Copyright (C) 2005 Gregor Richards +// +// 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 + +#include +#include
+#include
+#include
+#include +#include +#include +#include +#include +#include +#ifdef _MSC_VER +# include +# define finite _finite +#elif defined(__sun) || defined(sgi) +# include +#endif + +SG_USING_STD(string); + +#include "AIMultiplayer.hxx" + static string tempReg; + + +FGAIMultiplayer::FGAIMultiplayer(FGAIManager* mgr) { + manager = mgr; + _type_str = "multiplayer"; + _otype = otMultiplayer; + + _time_node = fgGetNode("/sim/time/elapsed-sec", true); + + //initialise values + speedN = speedE = rateH = rateR = rateP = 0.0; + raw_hdg = hdg; + raw_roll = roll; + raw_pitch = pitch; + raw_speed_east_deg_sec = speedE / ft_per_deg_lon; + raw_speed_north_deg_sec = speedN / ft_per_deg_lat; + raw_lon = damp_lon = pos.lon(); + raw_lat = damp_lat = pos.lat(); + raw_alt = damp_alt = pos.elev() / SG_FEET_TO_METER; + + //Exponentially weighted moving average time constants + speed_north_deg_sec_constant = speed_east_deg_sec_constant = 0.1; + alt_constant = 0.1; + lat_constant = 0.05; + lon_constant = 0.05; + hdg_constant = 0.1; + roll_constant = 0.1; + pitch_constant = 0.1; +} + + +FGAIMultiplayer::~FGAIMultiplayer() { +} + + +bool FGAIMultiplayer::init() { + return FGAIBase::init(); +} + +void FGAIMultiplayer::bind() { + FGAIBase::bind(); + props->setStringValue("callsign", company.c_str()); + + props->tie("controls/constants/roll", + SGRawValuePointer(&roll_constant)); + props->tie("controls/constants/pitch", + SGRawValuePointer(&pitch_constant)); + props->tie("controls/constants/hdg", + SGRawValuePointer(&hdg_constant)); + props->tie("controls/constants/altitude", + SGRawValuePointer(&alt_constant)); + /*props->tie("controls/constants/speedE", + SGRawValuePointer(&speed_east_deg_sec_constant)); + props->tie("controls/constants/speedN", + SGRawValuePointer(&speed_north_deg_sec_constant));*/ + props->tie("controls/constants/lat", + SGRawValuePointer(&lat_constant)); + props->tie("controls/constants/lon", + SGRawValuePointer(&lon_constant)); + props->tie("surface-positions/rudder-pos-norm", + SGRawValuePointer(&rudder)); + props->tie("surface-positions/elevator-pos-norm", + SGRawValuePointer(&elevator)); + props->tie("velocities/speedE-fps", + SGRawValuePointer(&speedE)); + + + props->setDoubleValue("sim/current-view/view-number", 1); + +} + +void FGAIMultiplayer::setCompany(string comp) { + company = comp; + if (props) + props->setStringValue("callsign", company.c_str()); + +} + +void FGAIMultiplayer::unbind() { + FGAIBase::unbind(); + + props->untie("controls/constants/roll"); + props->untie("controls/constants/pitch"); + props->untie("controls/constants/hdg"); + props->untie("controls/constants/altitude"); + /*props->untie("controls/constants/speedE"); + props->untie("controls/constants/speedN");*/ + props->untie("controls/constants/lat"); + props->untie("controls/constants/lon"); + props->untie("surface-positions/rudder-pos-norm"); + props->untie("surface-positions/elevator-pos-norm"); + props->untie("velocities/speedE-fps"); +} + + +void FGAIMultiplayer::update(double dt) { + + FGAIBase::update(dt); + Run(dt); + Transform(); +} + + +void FGAIMultiplayer::Run(double dt) { + + // strangely, this is called with a dt of 0 quite often + + SG_LOG( SG_GENERAL, SG_DEBUG, "AIMultiplayer::main loop dt " << dt ) ; + + //if (dt == 0) return; + + //FGAIMultiplayer::dt = dt; + + //double rhr, rha; // "real" heading radius/angle + + // get the current sim elapsed time + double time =_time_node->getDoubleValue(); //secs + + dt = 0; + + //calulate the time difference, dt. Then use this value to extrapolate position and orientation + dt = time - time_stamp; + + SG_LOG(SG_GENERAL, SG_DEBUG, "time: " + << time << " timestamp: " << time_stamp << " dt: " << dt << " freq Hz: " << 1/dt); + + // change heading/roll/pitch + raw_hdg = hdg + rateH * dt; + raw_roll = roll + rateR * dt; + raw_pitch = pitch + rateP * dt; + + //apply lowpass filters + hdg = (raw_hdg * hdg_constant) + (hdg * (1 - hdg_constant)); + roll = (raw_roll * roll_constant) + (roll * (1 - roll_constant)); + pitch = (raw_pitch * pitch_constant) + (pitch * (1 - pitch_constant)); + + /*cout << "raw roll " << raw_roll <<" damp hdg " << roll << endl; + cout << "raw hdg" << raw_hdg <<" damp hdg " << hdg << endl; + cout << "raw pitch " << raw_pitch <<" damp pitch " << pitch << endl;*/ + + // sanitize HRP + while (hdg < 0) hdg += 360; + while (hdg >= 360) hdg -= 360; + while (roll <= -180) roll += 360; + while (roll > 180) roll -= 360; + while (pitch <= -180) pitch += 360; + while (pitch > 180) pitch -= 360; + + // calculate the new accelerations by change in the rate of heading + /*rhr = sqrt(pow(accN,2) + pow(accE,2)); + rha = atan2(accN, accE); + rha += rateH * dt; + accN = sin(rha); + accE = cos(rha);*/ + + // calculate new speed by acceleration + speedN += accN * dt; + speedE += accE * dt; + speedD += accD * dt; + + // convert speed to degrees per second + // 1.686 + speed_north_deg_sec = speedN / ft_per_deg_lat; + speed_east_deg_sec = speedE / ft_per_deg_lon; + + // calculate new position by speed + raw_lat = pos.lat() + speed_north_deg_sec * dt; + raw_lon = pos.lon() + speed_east_deg_sec * dt ; + raw_alt = (pos.elev() / SG_FEET_TO_METER) + (speedD * dt); + + //apply lowpass filters if the difference is small + if ( fabs ( pos.lat() - raw_lat) < 0.001 ) { + SG_LOG(SG_GENERAL, SG_DEBUG,"lat lowpass filter"); + damp_lat = (raw_lat * lat_constant) + (damp_lat * (1 - lat_constant)); + }else { + // skip the filter + SG_LOG(SG_GENERAL, SG_DEBUG,"lat high pass filter"); + damp_lat = raw_lat; + } + + if ( fabs ( pos.lon() - raw_lon) < 0.001 ) { + SG_LOG(SG_GENERAL, SG_DEBUG,"lon lowpass filter"); + damp_lon = (raw_lon * lon_constant) + (damp_lon * (1 - lon_constant)); + }else { + // skip the filter + SG_LOG(SG_GENERAL, SG_DEBUG,"lon high pass filter"); + damp_lon = raw_lon; + } + + if ( fabs ( (pos.elev()/SG_FEET_TO_METER) - raw_alt) < 10 ) { + SG_LOG(SG_GENERAL, SG_DEBUG,"alt lowpass filter"); + damp_alt = (raw_alt * alt_constant) + (damp_alt * (1 - alt_constant)); + }else { + // skip the filter + SG_LOG(SG_GENERAL, SG_DEBUG,"alt high pass filter"); + damp_alt = raw_alt; + } + + // cout << "raw lat" << raw_lat <<" damp lat " << damp_lat << endl; + //cout << "raw lon" << raw_lon <<" damp lon " << damp_lon << endl; + //cout << "raw alt" << raw_alt <<" damp alt " << damp_alt << endl; + + // set new position + pos.setlat( damp_lat ); + pos.setlon( damp_lon ); + pos.setelev( damp_alt * SG_FEET_TO_METER ); + + //save the values + time_stamp = time; + + //###########################// + // do calculations for radar // + //###########################// + //double range_ft2 = UpdateRadar(manager); +} +void FGAIMultiplayer::setTimeStamp() +{ + // this function sets the timestamp as the sim elapsed time + time_stamp = _time_node->getDoubleValue(); //secs + + //calculate the elapsed time since the latst update for display purposes only + double elapsed_time = time_stamp - last_time_stamp; + + SG_LOG( SG_GENERAL, SG_DEBUG, " net input time s" << time_stamp << " freq Hz: " << 1/elapsed_time ) ; + + //save the values + last_time_stamp = time_stamp; +} + diff --git a/src/AIModel/AIMultiplayer.hxx b/src/AIModel/AIMultiplayer.hxx new file mode 100755 index 000000000..ec81195ef --- /dev/null +++ b/src/AIModel/AIMultiplayer.hxx @@ -0,0 +1,101 @@ +// FGAIMultiplayer - AIBase derived class creates an AI multiplayer aircraft +// +// Written by David Culp, started October 2003. +// With additions by Vivian Meazza +// +// Copyright (C) 2003 David P. Culp - davidculp2@comcast.net +// +// 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. + +#ifndef _FG_AIMultiplayer_HXX +#define _FG_AIMultiplayer_HXX + +#include "AIManager.hxx" +#include "AIBase.hxx" + +//#include +//#include + +#include +SG_USING_STD(string); + + +class FGAIMultiplayer : public FGAIBase { + + public: + FGAIMultiplayer(FGAIManager* mgr); + ~FGAIMultiplayer(); + + bool init(); + virtual void bind(); + virtual void unbind(); + void update(double dt); + + + void setSpeedN(double sn); + void setSpeedE(double se); + void setSpeedD(double sd); + void setAccN(double an); + void setAccE(double ae); + void setAccD(double ad); + void setRateH(double rh); + void setRateR(double rr); + void setRateP(double rp); + void setRudder( double r ) { rudder = r;} + void setElevator( double e ) { elevator = e; } + void setLeftAileron( double la ) { left_aileron = la; } + void setRightAileron( double ra ) { right_aileron = ra; } + void setTimeStamp(); + + inline SGPropertyNode *FGAIMultiplayer::getProps() { return props; } + + void setAcType(string ac) { acType = ac; }; + void setCompany(string comp); + + double dt; + double speedN, speedE, speedD; + double rateH, rateR, rateP; + double raw_hdg , raw_roll , raw_pitch ; + double raw_speed_north_deg_sec, raw_speed_east_deg_sec; + double raw_lat, damp_lat, lat_constant; + double raw_lon, damp_lon, lon_constant; + double raw_alt, damp_alt, alt_constant; + double hdg_constant, roll_constant, pitch_constant; + double speed_north_deg_sec_constant, speed_east_deg_sec_constant; + double speed_north_deg_sec, speed_east_deg_sec; + double accN, accE, accD; + double rudder, elevator, left_aileron, right_aileron; + double time_stamp, last_time_stamp; + + SGPropertyNode_ptr _time_node; + + void Run(double dt); + inline double sign(double x) { return (x < 0.0) ? -1.0 : 1.0; } + + string acType; + string company; +}; + +inline void FGAIMultiplayer::setSpeedN(double sn) { speedN = sn; } +inline void FGAIMultiplayer::setSpeedE(double se) { speedE = se; } +inline void FGAIMultiplayer::setSpeedD(double sd) { speedD = sd; } +inline void FGAIMultiplayer::setAccN(double an) { accN = an; } +inline void FGAIMultiplayer::setAccE(double ae) { accE = ae; } +inline void FGAIMultiplayer::setAccD(double ad) { accD = ad; } +inline void FGAIMultiplayer::setRateH(double rh) { rateH = rh; } +inline void FGAIMultiplayer::setRateR(double rr) { rateR = rr; } +inline void FGAIMultiplayer::setRateP(double rp) { rateP = rp; } + +#endif // _FG_AIMultiplayer_HXX diff --git a/src/AIModel/Makefile.am b/src/AIModel/Makefile.am index e02adc4f0..640daf505 100644 --- a/src/AIModel/Makefile.am +++ b/src/AIModel/Makefile.am @@ -5,6 +5,7 @@ libAIModel_a_SOURCES = \ AIManager.hxx AIManager.cxx \ AIBase.hxx AIBase.cxx \ AIAircraft.hxx AIAircraft.cxx \ + AIMultiplayer.hxx AIMultiplayer.cxx \ AIShip.hxx AIShip.cxx \ AIBallistic.hxx AIBallistic.cxx \ AIStorm.hxx AIStorm.cxx \ diff --git a/src/Main/globals.cxx b/src/Main/globals.cxx index 6a9b3f88c..8c3f23d3e 100644 --- a/src/Main/globals.cxx +++ b/src/Main/globals.cxx @@ -96,12 +96,12 @@ FGGlobals::FGGlobals() : FGGlobals::~FGGlobals() { delete soundmgr; + delete io; delete subsystem_mgr; delete event_mgr; delete initial_state; delete props; delete commands; - delete io; delete renderer; delete initial_waypoints; } diff --git a/src/MultiPlayer/mpmessages.hxx b/src/MultiPlayer/mpmessages.hxx index 3d34a4597..0e6b9468f 100644 --- a/src/MultiPlayer/mpmessages.hxx +++ b/src/MultiPlayer/mpmessages.hxx @@ -4,6 +4,8 @@ // Written by Duncan McCreanor, started February 2003. // duncan.mccreanor@airservicesaustralia.com // +// With minor additions be Vivian Meazza, January 2006 +// // Copyright (C) 2003 Airservices Australia // // This program is free software; you can redistribute it and/or @@ -47,14 +49,17 @@ const uint32_t PROTO_VER = 0x00010001; // 1.1 // Message identifiers #define CHAT_MSG_ID 1 #define UNUSABLE_POS_DATA_ID 2 -#define POS_DATA_ID 3 +#define OLD_POS_DATA_ID 3 +#define POS_DATA_ID 4 +#define PROP_MSG_ID 5 // XDR demands 4 byte alignment, but some compilers use8 byte alignment -// so it's safe to let the overall size of a netmork message be a +// so it's safe to let the overall size of a network message be a // multiple of 8! #define MAX_CALLSIGN_LEN 8 -#define MAX_CHAT_MSG_LEN 48 -#define MAX_MODEL_NAME_LEN 48 +#define MAX_CHAT_MSG_LEN 256 +#define MAX_MODEL_NAME_LEN 96 +#define MAX_PROPERTY_LEN 52 /** Aircraft position message */ typedef xdr_data2_t xdrPosition[3]; @@ -66,7 +71,7 @@ public: xdr_data_t Magic; // Magic Value xdr_data_t Version; // Protocoll version xdr_data_t MsgId; // Message identifier - xdr_data_t MsgLen; // absolue length of message + xdr_data_t MsgLen; // absolute length of message xdr_data_t ReplyAddress; // (player's receiver address xdr_data_t ReplyPort; // player's receiver port char Callsign[MAX_CALLSIGN_LEN]; // Callsign used by the player @@ -82,8 +87,39 @@ public: class T_PositionMsg { public: char Model[MAX_MODEL_NAME_LEN]; // Name of the aircraft model - xdrPosition PlayerPosition; // players position - xdrOrientation PlayerOrientation; // players orientation + xdr_data_t time; // Time when this packet was generated + xdr_data_t timeusec; // Microsecs when this packet was generated + xdr_data2_t lat; // Position, orientation, speed + xdr_data2_t lon; // ... + xdr_data2_t alt; // ... + xdr_data2_t hdg; // ... + xdr_data2_t roll; // ... + xdr_data2_t pitch; // ... + xdr_data2_t speedN; // ... + xdr_data2_t speedE; // ... + xdr_data2_t speedD; // ... + xdr_data_t accN; // acceleration N + xdr_data_t accE; // acceleration E + xdr_data_t accD; // acceleration D + xdr_data_t left_aileron; // control positions + xdr_data_t right_aileron; // control positions + xdr_data_t elevator; // ... + xdr_data_t rudder; // ... +// xdr_data_t rpms[6]; // RPMs of all of the motors + xdr_data_t rateH; // Rate of change of heading + xdr_data_t rateR; // roll + xdr_data_t rateP; // and pitch +// xdr_data_t dummy; // pad message length +}; + +// Property message +class T_PropertyMsg { +public: + char property[MAX_PROPERTY_LEN]; // the property name + xdr_data_t type; // the type + xdr_data2_t val; // and value +// xdr_data2_t dummy; // pad message length + }; #endif diff --git a/src/MultiPlayer/mpplayer.cxx b/src/MultiPlayer/mpplayer.cxx index cfaba2db4..00374ae21 100644 --- a/src/MultiPlayer/mpplayer.cxx +++ b/src/MultiPlayer/mpplayer.cxx @@ -5,6 +5,8 @@ // Written by Duncan McCreanor, started February 2003. // duncan.mccreanor@airservicesaustralia.com // +// With additions by Vivian Meazza, January 2006 +// // Copyright (C) 2003 Airservices Australia // Copyright (C) 2005 Oliver Schroeder // @@ -46,6 +48,7 @@ #include "mpplayer.hxx" + #include #if !(defined(_MSC_VER) || defined(__MINGW32__)) # include @@ -56,12 +59,16 @@ #include #include +#include +#include #include #include +#include +#include +#include #include
-#include - +//#include // These constants are provided so that the ident command can list file versions. const char sMPPLAYER_BID[] = "$Id$"; @@ -76,8 +83,14 @@ MPPlayer::MPPlayer() { m_Initialised = false; m_LastUpdate = 0; + m_LastUpdate = 0; + m_LastTime = 0; + m_LastUTime = 0; + m_Elapsed = 0; + m_TimeOffset = 0.0; m_Callsign = "none"; m_PlayerAddress.set("localhost", 0); + m_AIModel = NULL; } // MPPlayer::MPPlayer() ////////////////////////////////////////////////////////////////////// @@ -118,7 +131,7 @@ MPPlayer::Open if (!LocalPlayer) { try { - LoadModel(); + LoadAI(); } catch (...) { SG_LOG( SG_NETWORK, SG_ALERT, "Failed to load remote model '" << ModelName << "'." ); @@ -147,23 +160,86 @@ MPPlayer::Close(void) // Remove the model from the game if (m_Initialised && !m_LocalPlayer) { - // Disconnect the model from the transform, - // then the transform from the scene. - m_ModelTrans->removeKid(m_Model); - globals->get_scenery()->unregister_placement_transform(m_ModelTrans); - 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_lib()->flush1(); - // Assume that plib/ssg deletes the model and transform as their - // refcounts should be zero. + m_Initialised = 0; + + // get the model manager + FGAIManager *aiModelMgr = (FGAIManager *) globals->get_subsystem("ai_model"); + if (!aiModelMgr) { + SG_LOG( SG_NETWORK, SG_ALERT, "MPPlayer::Close - " + << "Cannot find AI model manager!" ); + return; + } + + // remove it + aiModelMgr->destroyObject(m_AIModel->getID()); + m_AIModel = NULL; } m_Initialised = false; m_Updated = false; m_LastUpdate = 0; + m_LastUpdate = 0; + m_LastTime = 0; + m_LastUTime = 0; + m_Elapsed = 0; + m_TimeOffset = 0.0; m_Callsign = "none"; } +/****************************************************************** +* Name: CheckTime +* Description: Checks if the time is valid for a position update +* and perhaps sets the time offset +******************************************************************/ +bool MPPlayer::CheckTime(int time, int timeusec) +{ + double curOffset; + + // set the offset + struct timeval tv; + int toff, utoff; + gettimeofday(&tv, NULL); + + // calculate the offset + toff = ((int) tv.tv_sec) - time; + utoff = ((int) tv.tv_usec) - timeusec; + while (utoff < 0) { + toff--; + utoff += 1000000; + } + + // set it + curOffset = ((double)toff) + (((double)utoff) / 1000000); + + if (m_LastUpdate == 0) { + // set the main offset + m_TimeOffset = curOffset; + m_Elapsed = 0; + return true; + } else { + // check it + if (time < m_LastTime || + (time == m_LastTime && timeusec <= m_LastUTime)) { + return false; + } else { + // set the current offset + m_LastOffset = curOffset; + // calculate the Hz + toff = time - m_LastTime; + utoff = timeusec - m_LastUTime; + while (utoff < 0) { + toff--; + utoff += 1000000; + } + m_Elapsed = ((double)toff) + (((double)utoff)/1000000); + + m_LastTime = time; + m_LastUTime = timeusec; + + return true; + } + } +} + /****************************************************************** * Name: SetPosition * Description: Updates position data held for this player and resets @@ -172,20 +248,133 @@ MPPlayer::Close(void) void MPPlayer::SetPosition ( - const sgQuat PlayerOrientation, - const sgdVec3 PlayerPosition + const double lat, const double lon, const double alt, + const double heading, const double roll, const double pitch, + const double speedN, const double speedE, const double speedD, + const double left_aileron, const double right_aileron, const double elevator, const double rudder, + //const double rpms[6], + const double rateH, const double rateR, const double rateP, + const double accN, const double accE, const double accD ) { + int toff, utoff; + // Save the position matrix and update time if (m_Initialised) { - sgdCopyVec3(m_ModelPosition, PlayerPosition); - sgCopyVec4(m_ModelOrientation, PlayerOrientation); + // calculate acceleration + /*if (m_Elapsed > 0) { + m_accN = (speedN - m_speedN) / m_Elapsed; + m_accE = (speedE - m_speedE) / m_Elapsed; + m_accD = (speedD - m_speedD) / m_Elapsed; + } else { + m_accN = 0; + m_accE = 0; + m_accD = 0; + }*/ + + // store the position + m_lat = lat; + m_lon = lon; + m_alt = alt; + m_hdg = heading; + m_roll = roll; + m_pitch = pitch; + m_speedN = speedN; + m_speedE = speedE; + m_speedD = speedD; + m_accN = accN; + m_accE = accE; + m_accD = accD; + m_left_aileron = left_aileron; + m_right_aileron = right_aileron; + m_elevator = elevator; + m_rudder = rudder; + + /*for (int i = 0; i < 6; i++) { + m_rpms[i] = rpms[i]; + } + m_rateH = rateH; + m_rateR = rateR; + m_rateP = rateP;*/ + + if (!m_LocalPlayer) { + m_AIModel->setLatitude(m_lat); + m_AIModel->setLongitude(m_lon); + m_AIModel->setAltitude(m_alt); + m_AIModel->setHeading(m_hdg); + m_AIModel->setBank(m_roll); + m_AIModel->setPitch(m_pitch); + m_AIModel->setSpeedN(m_speedN); + m_AIModel->setSpeedE(m_speedE); + m_AIModel->setSpeedD(m_speedD); + m_AIModel->setAccN(m_accN); + m_AIModel->setAccE(m_accE); + m_AIModel->setAccD(m_accD); + m_AIModel->setRateH(m_rateH); + m_AIModel->setRateR(m_rateR); + m_AIModel->setRateP(m_rateP); + + // set properties + SGPropertyNode *root = m_AIModel->getProps(); + root->getNode("surface-positions/left-aileron-pos-norm", true)->setDoubleValue(m_left_aileron); + root->getNode("surface-positions/right-aileron-pos-norm", true)->setDoubleValue(m_right_aileron); + root->getNode("surface-positions/elevator-pos-norm", true)->setDoubleValue(m_elevator); + root->getNode("surface-positions/rudder-pos-norm", true)->setDoubleValue(m_rudder); + /*root->getNode("engines/engine/rpm", true)->setDoubleValue(m_rpms[0]); + root->getNode("engines/engine[1]/rpm", true)->setDoubleValue(m_rpms[1]); + root->getNode("engines/engine[2]/rpm", true)->setDoubleValue(m_rpms[2]); + root->getNode("engines/engine[3]/rpm", true)->setDoubleValue(m_rpms[3]); + root->getNode("engines/engine[4]/rpm", true)->setDoubleValue(m_rpms[4]); + root->getNode("engines/engine[5]/rpm", true)->setDoubleValue(m_rpms[5]);*/ + + // Adjust by the last offset + //cout << "OFFSET: " << (m_LastOffset - m_TimeOffset) << endl; + + //m_AIModel->timewarp(m_LastOffset - m_TimeOffset); + + // set the timestamp for the data update (sim elapsed time (secs)) + m_AIModel->setTimeStamp(); + } + time(&m_LastUpdate); + m_Updated = true; } } +/****************************************************************** + * Name: SetProperty + * Description: Sets a property of this player. + ******************************************************************/ +void MPPlayer::SetProperty(string property, SGPropertyNode::Type type, double val) +{ + // get rid of any leading / + while (property[0] == '/') property = property.substr(1); + + // get our root node + SGPropertyNode *node = m_AIModel->getProps()->getNode(property.c_str(), true); + + // set the property + switch (type) { + case 2: + node->setBoolValue((bool) val); + break; + case 3: + node->setIntValue((int) val); + break; + case 4: + node->setLongValue((long) val); + break; + case 5: + node->setFloatValue((float) val); + break; + case 6: + default: + node->setDoubleValue(val); + } +} + /****************************************************************** * Name: Draw * Description: Updates the position for the player's model @@ -202,12 +391,37 @@ MPPlayer::Draw (void) // Peform an update if it has changed since the last update if (m_Updated) { - // Transform and update player model - sgMat4 orMat; - sgMakeIdentMat4(orMat); - sgQuatToMatrix(orMat, m_ModelOrientation); - m_ModelTrans->setTransform(m_ModelPosition, orMat); + /* + m_AIModel->setLatitude(m_lat); + m_AIModel->setLongitude(m_lon); + m_AIModel->setAltitude(m_alt); + m_AIModel->setHeading(m_hdg); + m_AIModel->setBank(m_roll); + m_AIModel->setPitch(m_pitch); + m_AIModel->setSpeedN(m_speedN); + m_AIModel->setSpeedE(m_speedE); + m_AIModel->_setVS_fps(m_speedU*60.0); // it needs input in fpm + m_AIModel->setRateH(m_rateH); + m_AIModel->setRateR(m_rateR); + m_AIModel->setRateP(m_rateP); + + // set properties + SGPropertyNode *root = m_AIModel->getProps(); + root->getNode("controls/flight/aileron", true)->setDoubleValue(m_aileron); + root->getNode("controls/flight/elevator", true)->setDoubleValue(m_elevator); + root->getNode("controls/flight/rudder", true)->setDoubleValue(m_rudder); + root->getNode("engines/engine/rpm", true)->setDoubleValue(m_rpms[0]); + root->getNode("engines/engine[1]/rpm", true)->setDoubleValue(m_rpms[1]); + root->getNode("engines/engine[2]/rpm", true)->setDoubleValue(m_rpms[2]); + root->getNode("engines/engine[3]/rpm", true)->setDoubleValue(m_rpms[3]); + root->getNode("engines/engine[4]/rpm", true)->setDoubleValue(m_rpms[4]); + root->getNode("engines/engine[5]/rpm", true)->setDoubleValue(m_rpms[5]); + + // Adjust by the last offset + m_AIModel->update(m_LastOffset - m_TimeOffset); + */ eResult = PLAYER_DATA_AVAILABLE; + // Clear the updated flag so that the position data // is only available if it has changed m_Updated = false; @@ -243,22 +457,31 @@ MPPlayer::CompareCallsign(const char *Callsign) const } /****************************************************************** -* Name: LoadModel -* Description: Loads the player's aircraft model. + * Name: LoadAI + * Description: Loads the AI model into the AI core. ******************************************************************/ void -MPPlayer::LoadModel (void) +MPPlayer::LoadAI(void) { - m_ModelTrans = new ssgPlacementTransform; - // Load the model - m_Model = globals->get_model_lib()->load_model( globals->get_fg_root(), - m_ModelName, globals->get_props(), globals->get_sim_time_sec() ); - m_Model->clrTraversalMaskBits( SSGTRAV_HOT ); - // Add model to transform - m_ModelTrans->addKid( m_Model ); - // Place on scene under aircraft branch - globals->get_scenery()->get_aircraft_branch()->addKid( m_ModelTrans ); - globals->get_scenery()->register_placement_transform( m_ModelTrans); + // set up the model info + FGAIModelEntity aiModel; + aiModel.m_type = "aircraft"; + aiModel.path = m_ModelName; + aiModel.acType = "Multiplayer"; + aiModel.company = m_Callsign; + + // then get the model manager + FGAIManager *aiModelMgr = (FGAIManager *) globals->get_subsystem("ai_model"); + if (!aiModelMgr) { + SG_LOG( SG_NETWORK, SG_ALERT, "MPPlayer::LoadAI - " + << "Cannot find AI model manager!" ); + return; + } + + // then get the model + fgSetBool("/sim/freeze/clock", true); + m_AIModel = (FGAIMultiplayer *) aiModelMgr->createMultiplayer(&aiModel); + fgSetBool("/sim/freeze/clock", false); } /****************************************************************** @@ -272,16 +495,36 @@ MPPlayer::FillPosMsg T_PositionMsg *PosMsg ) { + struct timeval tv; + gettimeofday(&tv, NULL); + FillMsgHdr(MsgHdr, POS_DATA_ID); strncpy(PosMsg->Model, m_ModelName.c_str(), MAX_MODEL_NAME_LEN); PosMsg->Model[MAX_MODEL_NAME_LEN - 1] = '\0'; - PosMsg->PlayerPosition[0] = XDR_encode_double (m_ModelPosition[0]); - PosMsg->PlayerPosition[1] = XDR_encode_double (m_ModelPosition[1]); - PosMsg->PlayerPosition[2] = XDR_encode_double (m_ModelPosition[2]); - PosMsg->PlayerOrientation[0] = XDR_encode_float (m_ModelOrientation[0]); - PosMsg->PlayerOrientation[1] = XDR_encode_float (m_ModelOrientation[1]); - PosMsg->PlayerOrientation[2] = XDR_encode_float (m_ModelOrientation[2]); - PosMsg->PlayerOrientation[3] = XDR_encode_float (m_ModelOrientation[3]); + PosMsg->time = XDR_encode_uint32 (tv.tv_sec); + PosMsg->timeusec = XDR_encode_uint32 (tv.tv_usec); + PosMsg->lat = XDR_encode_double (m_lat); + PosMsg->lon = XDR_encode_double (m_lon); + PosMsg->alt = XDR_encode_double (m_alt); + PosMsg->hdg = XDR_encode_double (m_hdg); + PosMsg->roll = XDR_encode_double (m_roll); + PosMsg->pitch = XDR_encode_double (m_pitch); + PosMsg->speedN = XDR_encode_double (m_speedN); + PosMsg->speedE = XDR_encode_double (m_speedE); + PosMsg->speedD = XDR_encode_double (m_speedD); + PosMsg->left_aileron = XDR_encode_float ((float) m_left_aileron); + PosMsg->right_aileron = XDR_encode_float ((float) m_right_aileron); + PosMsg->elevator = XDR_encode_float ((float) m_elevator); + PosMsg->rudder = XDR_encode_float ((float) m_rudder); + /*for (int i = 0; i < 6; i++) { + PosMsg->rpms[i] = XDR_encode_float ((float) m_rpms[i]); + }*/ + PosMsg->rateH = XDR_encode_float ((float) m_rateH); + PosMsg->rateR = XDR_encode_float ((float) m_rateR); + PosMsg->rateP = XDR_encode_float ((float) m_rateP); + PosMsg->accN = XDR_encode_float ((float) m_accN); + PosMsg->accE = XDR_encode_float ((float) m_accE); + PosMsg->accD = XDR_encode_float ((float) m_accD); } /****************************************************************** @@ -305,6 +548,9 @@ MPPlayer::FillMsgHdr case POS_DATA_ID: len = sizeof(T_MsgHdr) + sizeof(T_PositionMsg); break; + case PROP_MSG_ID: + len = sizeof(T_MsgHdr) + sizeof(T_PropertyMsg); + break; default: len = sizeof(T_MsgHdr); break; diff --git a/src/MultiPlayer/mpplayer.hxx b/src/MultiPlayer/mpplayer.hxx index b2e7fda85..c43b68967 100644 --- a/src/MultiPlayer/mpplayer.hxx +++ b/src/MultiPlayer/mpplayer.hxx @@ -44,7 +44,9 @@ #include #include #include +#include #include +#include #include STL_STRING SG_USING_STD(string); @@ -52,8 +54,7 @@ SG_USING_STD(string); // Number of seconds before a player is consider to be lost #define TIME_TO_LIVE 10 -class ssgEntity; -class ssgPlacementTransform; +class FGAIMultiplayer; class MPPlayer { @@ -81,11 +82,21 @@ public: const string &ModelName, const bool LocalPlayer); /** Closes the player connection */ void Close(void); + /** Checks if the time is valid for a position update and perhaps sets the time offset + */ + bool CheckTime(int time, int timeusec); /** Sets the positioning matrix held for this player - * @param PlayerPosMat4 Matrix for positioning player's aircraft */ - void SetPosition(const sgQuat PlayerOrientation, - const sgdVec3 PlayerPosition); + void SetPosition(const double lat, const double lon, const double alt, + const double heading, const double roll, const double pitch, + const double speedN, const double speedE, const double speedD, + const double left_aileron, const double right_aileron, const double elevator, const double rudder, + //const double rpms[6], + const double rateH, const double rateR, const double rateP, + const double accN, const double accE, const double accD); + /** Sets a property for this player + */ + void SetProperty(string property, SGPropertyNode::Type type, double val); /** Transform and place model for player */ TPlayerDataState Draw(void); @@ -109,18 +120,42 @@ public: */ void FillMsgHdr(T_MsgHdr *MsgHdr, const int iMsgId); private: - void LoadModel(void); // Loads the model of the aircraft + void LoadAI(void); // Loads the plane into the AI core bool m_Initialised; // True if object is initialised - sgdVec3 m_ModelPosition; // players global position on earth - sgQuat m_ModelOrientation; // players global orientation + + double m_lat; // location, orientation, etc... + double m_lon; // ... + double m_alt; // ... + double m_hdg; // ... + double m_roll; // ... + double m_pitch; // ... + double m_speedN; // ... + double m_speedE; // ... + double m_speedD; // ... + double m_accN; // ... + double m_accE; // ... + double m_accD; // ... + double m_left_aileron; // ... + double m_right_aileron; // ... + double m_elevator; // ... + double m_rudder; // ... + //double m_rpms[6]; // ... + double m_rateH; // ... + double m_rateR; // ... + double m_rateP; // ... + time_t m_LastUpdate; // last time update data received + int m_LastTime; // last seconds according to the packet + int m_LastUTime; // last microseconds according to the packet + double m_Elapsed; // Elapsed other-side time between responses + double m_TimeOffset; // the offset to aim for + double m_LastOffset; // the last offset we got bool m_Updated; // Set when the player data is updated string m_Callsign; // players callsign bool m_LocalPlayer; // true if player is the local player string m_ModelName; // Aircraft model name for player - ssgEntity *m_Model; // The player's loaded model netAddress m_PlayerAddress; // Address information for the player - ssgPlacementTransform *m_ModelTrans; // Model transform + FGAIMultiplayer *m_AIModel; // The AI model of this aircraft }; #endif diff --git a/src/MultiPlayer/multiplaymgr.cxx b/src/MultiPlayer/multiplaymgr.cxx index 6e1707558..75c37e71d 100644 --- a/src/MultiPlayer/multiplaymgr.cxx +++ b/src/MultiPlayer/multiplaymgr.cxx @@ -5,6 +5,8 @@ // Written by Duncan McCreanor, started February 2003. // duncan.mccreanor@airservicesaustralia.com // +// With minor additions by Vivian Meazza, January 2006 +// // Copyright (C) 2003 Airservices Australia // Copyright (C) 2005 Oliver Schroeder // @@ -70,6 +72,8 @@ FGMultiplayMgr::FGMultiplayMgr() m_RxPort = 0; m_Initialised = false; m_HaveServer = false; + + send_all_props= false; } // FGMultiplayMgr::FGMultiplayMgr() ////////////////////////////////////////////////////////////////////// @@ -84,6 +88,22 @@ FGMultiplayMgr::~FGMultiplayMgr() } // FGMultiplayMgr::~FGMultiplayMgr() ////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// +// Name: getSendAll +// Description: getter for send_all +///////////////////////////////////////////////////////////////////// +bool FGMultiplayMgr::getSendAllProps() { + return send_all_props; +} + +////////////////////////////////////////////////////////////////////// +// Name: setSendAll +// Description: setter for send_all +///////////////////////////////////////////////////////////////////// +void FGMultiplayMgr::setSendAllProps(bool s) { + send_all_props = s; +} + ////////////////////////////////////////////////////////////////////// // // Initialise object @@ -160,6 +180,7 @@ FGMultiplayMgr::init (void) return (false); } m_Initialised = true; + send_all_props= true; return (true); } // FGMultiplayMgr::init() ////////////////////////////////////////////////////////////////////// @@ -215,8 +236,13 @@ FGMultiplayMgr::Close (void) void FGMultiplayMgr::SendMyPosition ( - const sgQuat PlayerOrientation, - const sgdVec3 PlayerPosition + const double lat, const double lon, const double alt, + const double heading, const double roll, const double pitch, + const double speedN, const double speedE, const double speedD, + const double left_aileron, const double right_aileron, const double elevator, const double rudder, + //const double rpms[6], + const double rateH, const double rateR, const double rateP, + const double accN, const double accE, const double accD ) { T_MsgHdr MsgHdr; @@ -233,15 +259,59 @@ FGMultiplayMgr::SendMyPosition "FGMultiplayMgr::SendMyPosition - no server" ); return; } - m_LocalPlayer->SetPosition(PlayerOrientation, PlayerPosition); + m_LocalPlayer->SetPosition(lat, lon, alt, + heading, roll, pitch, + speedN, speedE, speedD, + left_aileron, right_aileron, elevator, rudder, + //rpms, + rateH, rateR, rateP, + accN, accE, accD); m_LocalPlayer->FillPosMsg(&MsgHdr, &PosMsg); memcpy(Msg, &MsgHdr, sizeof(T_MsgHdr)); memcpy(Msg + sizeof(T_MsgHdr), &PosMsg, sizeof(T_PositionMsg)); m_DataSocket->sendto (Msg, sizeof(T_MsgHdr) + sizeof(T_PositionMsg), 0, &m_Server); + SG_LOG( SG_NETWORK, SG_DEBUG, + "FGMultiplayMgr::SendMyPosition" ); + } // FGMultiplayMgr::SendMyPosition() ////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// +// +// Description: Sends the property data for the local player. +// +////////////////////////////////////////////////////////////////////// +void FGMultiplayMgr::SendPropMessage (const string &property, SGPropertyNode::Type type, double value) + + {SG_LOG( SG_NETWORK, SG_INFO, + "FGMultiplayMgr::Property: " << property << " Type " << type << " value " << value); + + T_MsgHdr MsgHdr; + T_PropertyMsg PropMsg; + unsigned int iNextBlockPosition = 0; + char Msg[sizeof(T_MsgHdr) + sizeof(T_PropertyMsg)]; + + if ((! m_Initialised) || (! m_HaveServer)) + { + return; + } + m_LocalPlayer->FillMsgHdr(&MsgHdr, PROP_MSG_ID); + + strncpy(PropMsg.property, property.c_str(), MAX_PROPERTY_LEN); + PropMsg.property[MAX_PROPERTY_LEN-1] = '\0'; + PropMsg.type = XDR_encode_uint32(type); + PropMsg.val = XDR_encode_double(value); + SG_LOG( SG_NETWORK, SG_INFO, + "FGMultiplayMgr::sending property message: " + << PropMsg.property << " " << PropMsg.type << " " << PropMsg.val); + + memcpy (Msg, &MsgHdr, sizeof(T_MsgHdr)); + memcpy (Msg + sizeof(T_MsgHdr), &PropMsg, sizeof(T_PropertyMsg)); + m_DataSocket->sendto (Msg, + sizeof(T_MsgHdr) + sizeof(T_PropertyMsg), 0, &m_Server); +} // FGMultiplayMgr::SendPropMessage () + ////////////////////////////////////////////////////////////////////// // // Name: SendTextMessage @@ -375,6 +445,9 @@ FGMultiplayMgr::ProcessData (void) case POS_DATA_ID: ProcessPosMsg ((char*) & Msg, SenderAddress); break; + case PROP_MSG_ID: + ProcessPropMsg ((char *) & Msg, SenderAddress); + break; default: SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - " @@ -401,27 +474,56 @@ FGMultiplayMgr::ProcessPosMsg T_PositionMsg* PosMsg; // Pointer to position message in received data T_MsgHdr* MsgHdr; // Pointer to header in received data bool ActivePlayer; - sgQuat Orientation; - sgdVec3 Position; + struct in_addr PlayerAddress; + int time, timeusec; + double lat, lon, alt; + double hdg, roll, pitch; + double speedN, speedE, speedD; + double left_aileron, right_aileron, elevator, rudder; + //double rpms[6]; + double rateH, rateR, rateP; + double accN, accE, accD; t_MPClientListIterator CurrentPlayer; ActivePlayer = false; MsgHdr = (T_MsgHdr *)Msg; - if (MsgHdr->MsgLen != sizeof(T_MsgHdr) + sizeof(T_PositionMsg)) + if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + sizeof(T_PositionMsg)) { SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - " << "Position message received with insufficient data" ); return; + } else if (MsgHdr->MsgLen > sizeof(T_MsgHdr) + sizeof(T_PositionMsg)) { + SG_LOG( SG_NETWORK, SG_ALERT, + "FGMultiplayMgr::MP_ProcessData - " + << "Position message received with more data than I can handle" ); } PosMsg = (T_PositionMsg *)(Msg + sizeof(T_MsgHdr)); - Position[0] = XDR_decode_double (PosMsg->PlayerPosition[0]); - Position[1] = XDR_decode_double (PosMsg->PlayerPosition[1]); - Position[2] = XDR_decode_double (PosMsg->PlayerPosition[2]); - Orientation[0] = XDR_decode_float (PosMsg->PlayerOrientation[0]); - Orientation[1] = XDR_decode_float (PosMsg->PlayerOrientation[1]); - Orientation[2] = XDR_decode_float (PosMsg->PlayerOrientation[2]); - Orientation[3] = XDR_decode_float (PosMsg->PlayerOrientation[3]); + time = XDR_decode_uint32 (PosMsg->time); + timeusec = XDR_decode_uint32 (PosMsg->timeusec); + lat = XDR_decode_double (PosMsg->lat); + lon = XDR_decode_double (PosMsg->lon); + alt = XDR_decode_double (PosMsg->alt); + hdg = XDR_decode_double (PosMsg->hdg); + roll = XDR_decode_double (PosMsg->roll); + pitch = XDR_decode_double (PosMsg->pitch); + speedN = XDR_decode_double (PosMsg->speedN); + speedE = XDR_decode_double (PosMsg->speedE); + speedD = XDR_decode_double (PosMsg->speedD); + left_aileron = XDR_decode_float (PosMsg->left_aileron); + right_aileron = XDR_decode_float (PosMsg->right_aileron); + elevator = XDR_decode_float (PosMsg->elevator); + rudder = XDR_decode_float (PosMsg->rudder); + /*for (int i = 0; i < 6; i++) { + rpms[i] = XDR_decode_float (PosMsg->rpms[i]); + }*/ + rateH = XDR_decode_float (PosMsg->rateH); + rateR = XDR_decode_float (PosMsg->rateR); + rateP = XDR_decode_float (PosMsg->rateP); + accN = XDR_decode_float (PosMsg->accN); + accE = XDR_decode_float (PosMsg->accE); + accD = XDR_decode_float (PosMsg->accD); + ////////////////////////////////////////////////// // Check if the player is already in the game // by using the Callsign @@ -432,8 +534,16 @@ FGMultiplayMgr::ProcessPosMsg { if ((*CurrentPlayer)->CompareCallsign(MsgHdr->Callsign)) { - // Player found. Update the data for the player. - (*CurrentPlayer)->SetPosition(Orientation, Position); + // Player found. Update the data for the player if the timestamp is OK + if ((*CurrentPlayer)->CheckTime(time, timeusec)) + (*CurrentPlayer)->SetPosition(lat, lon, alt, + hdg, roll, pitch, + speedN, speedE, speedD, + left_aileron, right_aileron, elevator, rudder, + //rpms, + rateH, rateR, rateP, + accN, accE, accD + ); ActivePlayer = true; } } // for (...) @@ -448,8 +558,19 @@ FGMultiplayMgr::ProcessPosMsg NewPlayer = new MPPlayer; NewPlayer->Open(SenderAddress.getHost(), MsgHdr->ReplyPort, MsgHdr->Callsign, PosMsg->Model, false); - NewPlayer->SetPosition(Orientation, Position); + if (NewPlayer->CheckTime(time, timeusec)) + NewPlayer->SetPosition(lat, lon, alt, + hdg, roll, pitch, + speedN, speedE, speedD, + left_aileron, right_aileron, elevator, rudder, + //rpms, + rateH, rateR, rateP, + accN, accE, accD); m_MPClientList.push_back (NewPlayer); + + // if we have a new player then we need to send all our properties + send_all_props = true; + } // FGMultiplayMgr::ProcessPosMsg() ////////////////////////////////////////////////////////////////////// @@ -470,19 +591,77 @@ FGMultiplayMgr::ProcessChatMsg T_MsgHdr* MsgHdr; // Pointer to header in received data MsgHdr = (T_MsgHdr *)Msg; - if (MsgHdr->MsgLen != sizeof(T_MsgHdr) + sizeof(T_ChatMsg)) + if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + 1) { SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - " << "Chat message received with insufficient data" ); return; } + + char *MsgBuf = new char[MsgHdr->MsgLen - sizeof(T_MsgHdr)]; + strncpy(MsgBuf, ((T_ChatMsg *)(Msg + sizeof(T_MsgHdr)))->Text, MsgHdr->MsgLen - sizeof(T_MsgHdr)); + MsgBuf[MsgHdr->MsgLen - sizeof(T_MsgHdr) - 1] = '\0'; + ChatMsg = (T_ChatMsg *)(Msg + sizeof(T_MsgHdr)); SG_LOG ( SG_NETWORK, SG_ALERT, - "Chat [" << MsgHdr->Callsign << "]" << " " << ChatMsg->Text << endl); + "Chat [" << MsgHdr->Callsign << "]" << " " << MsgBuf << endl); + delete [] MsgBuf; } // FGMultiplayMgr::ProcessChatMsg () ////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// +// +// handle a property message +// +////////////////////////////////////////////////////////////////////// +void FGMultiplayMgr::ProcessPropMsg ( const char *Msg, netAddress & SenderAddress ) +{ + T_PropertyMsg* PropMsg; // Pointer to the message itself + T_MsgHdr* MsgHdr; // Pointer to the message header + t_MPClientListIterator CurrentPlayer; + + MsgHdr = (T_MsgHdr *) Msg; + + if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + sizeof(T_PropertyMsg)) { + SG_LOG( SG_NETWORK, SG_ALERT, + "FGMultiplayMgr::MP_ProcessData - " + << "Properties message received with insufficient data" ); + return; + } else if (MsgHdr->MsgLen > sizeof(T_MsgHdr) + sizeof(T_PropertyMsg)) { + SG_LOG( SG_NETWORK, SG_ALERT, + "FGMultiplayMgr::MP_ProcessData - " + << "Properties message received with more data than I know how to handle" ); + return; + } + + PropMsg = (T_PropertyMsg *)(Msg + sizeof(T_MsgHdr)); + + ////////////////////////////////////////////////// + // Check if the player is already in the game + // by using the Callsign, but don't activate + // new players (they need to send a position + // message) + ////////////////////////////////////////////////// + for (CurrentPlayer = m_MPClientList.begin (); + CurrentPlayer != m_MPClientList.end (); + CurrentPlayer++) + { + if ((*CurrentPlayer)->CompareCallsign(MsgHdr->Callsign)) + { + // Player found. Update the data for the player. + (*CurrentPlayer)->SetProperty(PropMsg->property, + (SGPropertyNode::Type) XDR_decode_uint32(PropMsg->type), + XDR_decode_double(PropMsg->val)); + SG_LOG( SG_NETWORK, SG_INFO, + "FGMultiplayMgr::MP_ProcessData - " + << PropMsg->property + << (SGPropertyNode::Type) XDR_decode_uint32(PropMsg->type) + << XDR_decode_double(PropMsg->val) ); + } + } // for (...) +} + ////////////////////////////////////////////////////////////////////// // // For each active player, tell the player object diff --git a/src/MultiPlayer/multiplaymgr.hxx b/src/MultiPlayer/multiplaymgr.hxx index 63add6b77..857557cfd 100644 --- a/src/MultiPlayer/multiplaymgr.hxx +++ b/src/MultiPlayer/multiplaymgr.hxx @@ -44,6 +44,7 @@ SG_USING_STD(string); SG_USING_STD(vector); #include +#include #include #include
@@ -59,14 +60,28 @@ public: bool init(void); void Close(void); // transmitter - void SendMyPosition (const sgQuat PlayerOrientation, - const sgdVec3 PlayerPosition); + void SendMyPosition (const double lat, const double lon, const double alt, + const double heading, const double roll, const double pitch, + const double speedN, const double speedE, const double speedD, + const double left_aileron, const double right_aileron, const double elevator, const double rudder, + //const double rpms[6], + const double rateH, const double rateR, const double rateP, + const double accN, const double accE, const double accD + ); + void SendPropMessage (const string &property, SGPropertyNode::Type type, double value); void SendTextMessage (const string &sMsgText) const; // receiver void ProcessData(void); void ProcessPosMsg ( const char *Msg, netAddress & SenderAddress ); void ProcessChatMsg ( const char *Msg, netAddress & SenderAddress ); + void ProcessPropMsg ( const char *Msg, netAddress & SenderAddress ); void Update(void); + + /* getters/setters */ + bool getSendAllProps(); + void setSendAllProps(bool s); + bool send_all_props; + private: typedef vector t_MPClientList; typedef t_MPClientList::iterator t_MPClientListIterator; diff --git a/src/Network/multiplay.cxx b/src/Network/multiplay.cxx index ebf511f88..3511f73f1 100644 --- a/src/Network/multiplay.cxx +++ b/src/Network/multiplay.cxx @@ -3,6 +3,8 @@ // Written by Diarmuid Tyson, started February 2003. // diarmuid.tyson@airservicesaustralia.com // +// With addtions by Vivian Meazza, January 2006 +// // Copyright (C) 2003 Airservices Australia // // This program is free software; you can redistribute it and/or @@ -29,6 +31,8 @@ #include STL_STRING #include +#include +#include #include #include @@ -53,22 +57,123 @@ const char sFG_MULTIPLAY_HID[] = FG_MULTIPLAY_HID; ******************************************************************/ FGMultiplay::FGMultiplay (const string &dir, const int rate, const string &host, const int port) { - set_hz(rate); + last_time = 0; + last_speedN = last_speedE = last_speedD = 0; + calcaccN = calcaccE = calcaccD = 0; + set_hz(rate); - set_direction(dir); + set_direction(dir); - if (get_direction() == SG_IO_IN) { + if (get_direction() == SG_IO_IN) { - fgSetInt("/sim/multiplay/rxport", port); - fgSetString("/sim/multiplay/rxhost", host.c_str()); + fgSetInt("/sim/multiplay/rxport", port); + fgSetString("/sim/multiplay/rxhost", host.c_str()); - } else if (get_direction() == SG_IO_OUT) { + } else if (get_direction() == SG_IO_OUT) { - fgSetInt("/sim/multiplay/txport", port); - fgSetString("/sim/multiplay/txhost", host.c_str()); + fgSetInt("/sim/multiplay/txport", port); + fgSetString("/sim/multiplay/txhost", host.c_str()); - } + } + lat_n = fgGetNode("/position/latitude-deg", true); + lon_n = fgGetNode("/position/longitude-deg", true); + alt_n = fgGetNode("/position/altitude-ft", true); + heading_n = fgGetNode("/orientation/heading-deg", true); + roll_n = fgGetNode("/orientation/roll-deg", true); + pitch_n = fgGetNode("/orientation/pitch-deg", true); + speedN_n = fgGetNode("/velocities/speed-north-fps", true); + speedE_n = fgGetNode("/velocities/speed-east-fps", true); + speedD_n = fgGetNode("/velocities/speed-down-fps", true); + left_aileron_n = fgGetNode("/surface-positions/left-aileron-pos-norm", true); + right_aileron_n = fgGetNode("/surface-positions/right-aileron-pos-norm", true); + elevator_n = fgGetNode("/surface-positions/elevator-pos-norm", true); + rudder_n = fgGetNode("/surface-positions/rudder-pos-norm", true); + /*rpms_n[0] = fgGetNode("/engines/engine/rpm", true); + rpms_n[1] = fgGetNode("/engines/engine[1]/rpm", true); + rpms_n[2] = fgGetNode("/engines/engine[2]/rpm", true); + rpms_n[3] = fgGetNode("/engines/engine[3]/rpm", true); + rpms_n[4] = fgGetNode("/engines/engine[4]/rpm", true); + rpms_n[5] = fgGetNode("/engines/engine[5]/rpm", true);*/ + rateH_n = fgGetNode("/orientation/yaw-rate-degps", true); + rateR_n = fgGetNode("/orientation/roll-rate-degps", true); + rateP_n = fgGetNode("/orientation/pitch-rate-degps", true); + + SGPropertyNode_ptr n = fgGetNode("/controls/flight/slats",true); + _node_cache *c = new _node_cache( n->getDoubleValue(), n ); + props["controls/flight/slats"] = c; + + n = fgGetNode("/controls/flight/speedbrake", true); + c = new _node_cache( n->getDoubleValue(), n ); + props["controls/flight/speedbrake"] = c; + + n = fgGetNode("/controls/flight/spoilers", true); + c = new _node_cache( n->getDoubleValue(), n ); + props["controls/flight/spoilers"] = c; + + n = fgGetNode("/controls/gear/gear-down", true); + c = new _node_cache( n->getDoubleValue(), n ); + props["controls/gear/gear-down"] = c; + + n = fgGetNode("/controls/lighting/nav-lights", true); + c = new _node_cache( n->getDoubleValue(), n ); + props["controls/lighting/nav-lights"] = c; + + n = fgGetNode("/surface-positions/flap-pos-norm", true); + c = new _node_cache( n->getDoubleValue(), n ); + props["surface-positions/flap-pos-norm"] = c; + + n = fgGetNode("/surface-positions/speedbrake-pos-norm", true); + c = new _node_cache( n->getDoubleValue(), n ); + props["surface-positions/speedbrake-pos-norm"] = c; + + for (int i = 0; i < 6; i++) + { + char *s = new char[32]; + + snprintf(s, 32, "engines/engine[%i]/n1", i); + n = fgGetNode(s, true); + c = new _node_cache( n->getDoubleValue(), n ); + props["s"] = c; + + snprintf(s, 32, "engines/engine[%i]/n2", i); + n = fgGetNode(s, true); + c = new _node_cache( n->getDoubleValue(), n ); + props[s] = c; + + snprintf(s, 32, "engines/engine[%i]/rpm", i); + n = fgGetNode(s, true); + c = new _node_cache( n->getDoubleValue(), n ); + props[s] = c; + + delete [] s; + } + + for (int j = 0; j < 5; j++) + { + char *s = new char[32]; + + snprintf(s, 32, "gear/gear[%i]/compression-norm", j); + n = fgGetNode(s, true); + c = new _node_cache( n->getDoubleValue(), n ); + props["s"] = c; + + snprintf(s, 32, "gear/gear[%i]/position-norm", j); + n = fgGetNode(s, true); + c = new _node_cache( n->getDoubleValue(), n ); + props[s] = c; +#if 0 + snprintf(s, 32, "gear/gear[%i]/rollspeed-ms", j); + n = fgGetNode(s, true); + c = new _node_cache( n->getDoubleValue(), n ); + props[s] = c; +#endif + delete [] s; + } + + n = fgGetNode("gear/tailhook/position-norm", true); + c = new _node_cache( n->getDoubleValue(), n ); + props["gear/tailhook/position-norm"] = c; } @@ -77,6 +182,7 @@ FGMultiplay::FGMultiplay (const string &dir, const int rate, const string &host, * Description: Destructor. ******************************************************************/ FGMultiplay::~FGMultiplay () { + props.clear(); } @@ -87,9 +193,9 @@ FGMultiplay::~FGMultiplay () { bool FGMultiplay::open() { if ( is_enabled() ) { - SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel " - << "is already in use, ignoring" ); - return false; + SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel " + << "is already in use, ignoring" ); + return false; } set_enabled(true); @@ -111,17 +217,74 @@ bool FGMultiplay::process() { } else if (get_direction() == SG_IO_OUT) { - sgMat4 posTrans; - globals->get_aircraft_model()->get3DModel()->getTransform()->getTransform(posTrans); - Point3D center = globals->get_scenery()->get_center(); - sgdVec3 PlayerPosition; - sgdSetVec3(PlayerPosition, posTrans[3][0] + center[0], - posTrans[3][1] + center[1], posTrans[3][2] + center[2]); - sgQuat PlayerOrientation; - sgMatrixToQuat(PlayerOrientation, posTrans); + double accN, accE, accD; + string fdm = fgGetString("/sim/flight-model"); - globals->get_multiplayer_mgr()->SendMyPosition(PlayerOrientation, PlayerPosition); + if(fdm == "jsb"){ + calcAcc(speedN_n->getDoubleValue(), + speedE_n->getDoubleValue(), + speedD_n->getDoubleValue()); + accN = calcaccN; + accE = calcaccE; + accD = calcaccD; + }else{ + SG_LOG(SG_GENERAL, SG_DEBUG," not doing acc calc" << fdm); + accN = fgGetDouble("/accelerations/ned/north-accel-fps_sec"); + accE = fgGetDouble("/accelerations/ned/east-accel-fps_sec"); + accD = fgGetDouble("/accelerations/ned/down-accel-fps_sec"); + } + globals->get_multiplayer_mgr()->SendMyPosition( + lat_n->getDoubleValue(), + lon_n->getDoubleValue(), + alt_n->getDoubleValue(), + heading_n->getDoubleValue(), + roll_n->getDoubleValue(), + pitch_n->getDoubleValue(), + speedN_n->getDoubleValue(), + speedE_n->getDoubleValue(), + speedD_n->getDoubleValue(), + left_aileron_n->getDoubleValue(), + right_aileron_n->getDoubleValue(), + elevator_n->getDoubleValue(), + rudder_n->getDoubleValue(), + rateH_n->getDoubleValue(), + rateR_n->getDoubleValue(), + rateP_n->getDoubleValue(), + accN, accE, accD); + + // check for changes + for (propit = props.begin(); propit != props.end(); propit++) + { + double val = propit->second->val; + double curr_val = propit->second->node->getDoubleValue(); + if (curr_val < val * 0.99 || curr_val > val * 1.01 ) + { + SGPropertyNode::Type type = propit->second->node->getType(); + propit->second->val = val = curr_val; + globals->get_multiplayer_mgr()->SendPropMessage(propit->first, type, val); + //cout << "Prop " << propit->first <<" type " << type << " val " << val << endl; + } else { + // cout << "no change" << endl; + } + } + + // send all properties when necessary + // FGMultiplayMgr::getSendAllProps(); + bool send_all = globals->get_multiplayer_mgr()->getSendAllProps(); + //cout << "send_all in " << send; + if (send_all){ + SG_LOG( SG_NETWORK, SG_ALERT, + "FGMultiplay::sending ALL property messages" ); + for (propit = props.begin(); propit != props.end(); propit++) { + SGPropertyNode::Type type = propit->second->node->getType(); + double val = propit->second->val; + globals->get_multiplayer_mgr()->SendPropMessage(propit->first, type, val); + } + send_all = false; + globals->get_multiplayer_mgr()->setSendAllProps(send_all); + } + //cout << " send_all out " << s << endl; } return true; @@ -148,3 +311,39 @@ bool FGMultiplay::close() { return true; } +/****************************************************************** + * Name: CalcAcc + * Description: Calculate accelerations given speedN, speedE, speedD + ******************************************************************/ +void FGMultiplay::calcAcc(double speedN, double speedE, double speedD) +{ + double time, dt; //secs + /*double accN, accE, accD; */ //fps2 + + dt = 0; + + time = fgGetDouble("/sim/time/elapsed-sec"); + + dt = time-last_time; + + SG_LOG(SG_GENERAL, SG_DEBUG," doing acc calc" + <<"time: "<< time << " last " << last_time << " dt " << dt ); + + //calculate the accelerations + calcaccN = (speedN - last_speedN)/dt; + calcaccE = (speedE - last_speedE)/dt; + calcaccD = (speedD - last_speedD)/dt; + + //set the properties + /*fgSetDouble("/accelerations/ned/north-accel-fps_sec",accN); + fgSetDouble("/accelerations/ned/east-accel-fps_sec",accE); + fgSetDouble("/accelerations/ned/down-accel-fps_sec",accN);*/ + + //save the values + last_time = time; + last_speedN = speedN; + last_speedE = speedE; + last_speedD = speedD; + +}// end calcAcc + diff --git a/src/Network/multiplay.hxx b/src/Network/multiplay.hxx index 2b673e092..a36cd3827 100644 --- a/src/Network/multiplay.hxx +++ b/src/Network/multiplay.hxx @@ -3,6 +3,8 @@ // Written by Diarmuid Tyson, started February 2003. // diarmuid.tyson@airservicesaustralia.com // +// With additions by Vivian Meazza, January 2006 +// // Copyright (C) 2003 Airservices Australia // // This program is free software; you can redistribute it and/or @@ -56,6 +58,8 @@ SG_USING_STD(string); * ******************************************************************/ +#include + class FGMultiplay : public FGProtocol { public: @@ -65,21 +69,42 @@ public: /** Destructor. */ ~FGMultiplay (); - /** Enables the FGMultiplay object - */ + /** Enables the FGMultiplay object. */ bool open(); - /** Tells the multiplayer_mgr to send/receive data. - */ + /** Tells the multiplayer_mgr to send/receive data. */ bool process(); - /** Closes the multiplayer_mgr. - */ + /** Closes the multiplayer_mgr. */ bool close(); + private: + struct _node_cache { + double val; + SGPropertyNode_ptr node; + _node_cache(double v, SGPropertyNode_ptr n) { + val = v; node = n; + }; + }; + /** calculate accelerations + */ + void calcAcc(double speedN, double speedE, double speedD); + + double last_time ; //sec + double last_speedN, last_speedE, last_speedD; //fps + double calcaccN, calcaccE, calcaccD; //fps2 + + SGPropertyNode *lat_n, *lon_n, *alt_n; + SGPropertyNode *heading_n, *pitch_n, *roll_n; + SGPropertyNode *speedN_n, *speedE_n, *speedD_n; + SGPropertyNode *left_aileron_n, *right_aileron_n; + SGPropertyNode *elevator_n, *rudder_n; + // SGPropertyNode *rpms[5]; + SGPropertyNode *rateH_n, *rateR_n, *rateP_n; + + map props; + map::iterator propit; }; - - #endif // _FG_MULTIPLAY_HXX