Stuart Buchanan <stuart_d_buchanan@yahoo.co.uk>
+ 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.
_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;
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();
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 );
no_roll = false;
}
+inline void FGAIBase::setPitch( double newpitch ) {
+ pitch = tgt_pitch = newpitch;
+}
+
inline void FGAIBase::setLongitude( double longitude ) {
pos.setlon( longitude );
}
#include "AIThermal.hxx"
#include "AICarrier.hxx"
#include "AIStatic.hxx"
+#include "AIMultiplayer.hxx"
SG_USING_STD(list);
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 ) {
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 );
--- /dev/null
+// 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 <config.h>
+#endif
+
+#include <simgear/math/point3d.hxx>
+#include <Main/fg_props.hxx>
+#include <Main/globals.hxx>
+#include <Main/viewer.hxx>
+#include <Scenery/scenery.hxx>
+#include <Scenery/tilemgr.hxx>
+#include <simgear/route/waypoint.hxx>
+#include <string>
+#include <math.h>
+#include <time.h>
+#ifdef _MSC_VER
+# include <float.h>
+# define finite _finite
+#elif defined(__sun) || defined(sgi)
+# include <ieeefp.h>
+#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<double>(&roll_constant));
+ props->tie("controls/constants/pitch",
+ SGRawValuePointer<double>(&pitch_constant));
+ props->tie("controls/constants/hdg",
+ SGRawValuePointer<double>(&hdg_constant));
+ props->tie("controls/constants/altitude",
+ SGRawValuePointer<double>(&alt_constant));
+ /*props->tie("controls/constants/speedE",
+ SGRawValuePointer<double>(&speed_east_deg_sec_constant));
+ props->tie("controls/constants/speedN",
+ SGRawValuePointer<double>(&speed_north_deg_sec_constant));*/
+ props->tie("controls/constants/lat",
+ SGRawValuePointer<double>(&lat_constant));
+ props->tie("controls/constants/lon",
+ SGRawValuePointer<double>(&lon_constant));
+ props->tie("surface-positions/rudder-pos-norm",
+ SGRawValuePointer<double>(&rudder));
+ props->tie("surface-positions/elevator-pos-norm",
+ SGRawValuePointer<double>(&elevator));
+ props->tie("velocities/speedE-fps",
+ SGRawValuePointer<double>(&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;
+}
+
--- /dev/null
+// 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 <Traffic/SchedFlight.hxx>
+//#include <Traffic/Schedule.hxx>
+
+#include <string>
+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
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 \
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;
}
// 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
// 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];
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
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
// 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
//
#include "mpplayer.hxx"
+
#include <stdlib.h>
#if !(defined(_MSC_VER) || defined(__MINGW32__))
# include <netdb.h>
#include <plib/netSocket.h>
#include <plib/sg.h>
+#include <simgear/math/sg_geodesy.hxx>
+#include <simgear/props/props.hxx>
#include <simgear/scene/model/modellib.hxx>
#include <simgear/scene/model/placementtrans.hxx>
+#include <AIModel/AIBase.hxx>
+#include <AIModel/AIManager.hxx>
+#include <AIModel/AIMultiplayer.hxx>
#include <Main/globals.hxx>
-#include <Scenery/scenery.hxx>
-
+//#include <Scenery/scenery.hxx>
// These constants are provided so that the ident command can list file versions.
const char sMPPLAYER_BID[] = "$Id$";
{
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()
//////////////////////////////////////////////////////////////////////
if (!LocalPlayer)
{
try {
- LoadModel();
+ LoadAI();
} catch (...) {
SG_LOG( SG_NETWORK, SG_ALERT,
"Failed to load remote model '" << ModelName << "'." );
// 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
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
// 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;
}
/******************************************************************
-* 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);
}
/******************************************************************
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);
}
/******************************************************************
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;
#include <plib/sg.h>
#include <plib/netSocket.h>
#include <simgear/io/sg_socket_udp.hxx>
+#include <simgear/props/props.hxx>
#include <time.h>
+#include <sys/time.h>
#include STL_STRING
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
{
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);
*/
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
// 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
//
m_RxPort = 0;
m_Initialised = false;
m_HaveServer = false;
+
+ send_all_props= false;
} // 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
return (false);
}
m_Initialised = true;
+ send_all_props= true;
return (true);
} // FGMultiplayMgr::init()
//////////////////////////////////////////////////////////////////////
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;
"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
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 - "
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
{
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 (...)
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()
//////////////////////////////////////////////////////////////////////
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
SG_USING_STD(vector);
#include <simgear/compiler.h>
+#include <simgear/props/props.hxx>
#include <plib/netSocket.h>
#include <Main/globals.hxx>
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<MPPlayer*> t_MPClientList;
typedef t_MPClientList::iterator t_MPClientListIterator;
// 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
#include STL_STRING
#include <iostream>
+#include <map>
+#include <string>
#include <simgear/debug/logstream.hxx>
#include <simgear/scene/model/placement.hxx>
******************************************************************/
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;
}
* Description: Destructor.
******************************************************************/
FGMultiplay::~FGMultiplay () {
+ props.clear();
}
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);
} 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");
+
+ 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(PlayerOrientation, PlayerPosition);
+ 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;
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
+
// 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
*
******************************************************************/
+#include <simgear/props/props.hxx>
+
class FGMultiplay : public FGProtocol {
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<string,struct _node_cache*> props;
+ map<string,struct _node_cache*>::iterator propit;
};
-
-
#endif // _FG_MULTIPLAY_HXX