AC_DEFINE([FG_NDEBUG], 1, [Define for no logging output])
fi
-# Specify if we want to build with Multiplayer support
-# default to with_network=yes
-AC_ARG_WITH(multiplayer, [ --with-multiplayer Include multiplayer support [default=yes]])
-
-if test "x$with_multiplayer" = "xno"; then
- echo "Building without any kind of multiplayer support"
-else
- echo "Building with multiplayer support"
- AC_DEFINE([FG_MPLAYER_AS], 1, [Define to build with multiplayer support])
-fi
-AM_CONDITIONAL(ENABLE_MPLAYER_AS, test "x$with_multiplayer" != "xno")
-
AC_ARG_ENABLE(sp_fdms, [ --enable-sp-fdms Include special purpose Flight Models], [enable_sp_fdms="$enableval"] )
AC_DEFINE([ENABLE_SP_FMDS], test "x$enable_sp_fdms" = "xyes", [Define to include special purpose FDMs])
AM_CONDITIONAL(ENABLE_SP_FDM, test "x$enable_sp_fdms" != "xno")
invisible = true;
no_roll = true;
life = 900;
- index = 0;
delete_me = false;
}
globals->get_scenery()->unregister_placement_transform(aip.getTransform());
globals->get_scenery()->get_scene_graph()->removeKid(aip.getSceneGraph());
}
- SGPropertyNode *root = globals->get_props()->getNode("ai/models", true);
- root->removeChild(getTypeString(), index);
+ if (props) {
+ SGPropertyNode* parent = props->getParent();
+ if (parent)
+ parent->removeChild(props->getName(), props->getIndex());
+ }
delete fp;
- fp = NULL;
+ fp = 0;
}
SGPropertyNode *root = globals->get_props()->getNode("ai/models", true);
- index = manager->getNum(_otype) - 1;
+ unsigned index = root->getChildren(getTypeString()).size();
+
props = root->getNode(getTypeString(), index, true);
if (!model_path.empty()) {
void CalculateMach();
double UpdateRadar(FGAIManager* manager);
- int index;
-
static int _newAIModelID();
private:
#include "AIMultiplayer.hxx"
FGAIManager::FGAIManager() {
- for (int i=0; i < FGAIBase::MAX_OBJECTS; i++)
- numObjects[i] = 0;
_dt = 0.0;
- scenario_filename = "";
+ mNumAiModels = 0;
}
FGAIManager::~FGAIManager() {
void FGAIManager::bind() {
root = globals->get_props()->getNode("ai/models", true);
- root->tie("count", SGRawValuePointer<int>(&numObjects[0]));
+ root->tie("count", SGRawValueMethods<FGAIManager, int>(*this,
+ &FGAIManager::getNumAiObjects));
}
void FGAIManager::update(double dt) {
- // initialize these for finding nearest thermals
- range_nearest = 10000.0;
- strength = 0.0;
- FGTrafficManager *tmgr = (FGTrafficManager*) globals->get_subsystem("Traffic Manager");
-
- if (!enabled)
- return;
-
- _dt = dt;
-
- ai_list_iterator ai_list_itr = ai_list.begin();
- while(ai_list_itr != ai_list.end()) {
- if ((*ai_list_itr)->getDie()) {
- tmgr->release((*ai_list_itr)->getID());
- --numObjects[(*ai_list_itr)->getType()];
- --numObjects[0];
- (*ai_list_itr)->unbind();
- if ( ai_list_itr == ai_list.begin() ) {
- ai_list.erase(ai_list_itr);
- ai_list_itr = ai_list.begin();
- continue;
- } else {
- ai_list.erase(ai_list_itr--);
- }
- } else {
- fetchUserState();
- if ((*ai_list_itr)->isa(FGAIBase::otThermal)) {
- FGAIBase *base = *ai_list_itr;
- processThermal((FGAIThermal*)base);
- } else {
- (*ai_list_itr)->update(_dt);
- }
- }
- ++ai_list_itr;
- }
- wind_from_down_node->setDoubleValue( strength ); // for thermals
+ // initialize these for finding nearest thermals
+ range_nearest = 10000.0;
+ strength = 0.0;
+ if (!enabled)
+ return;
+
+ FGTrafficManager *tmgr = (FGTrafficManager*) globals->get_subsystem("Traffic Manager");
+ _dt = dt;
+
+ ai_list_iterator ai_list_itr = ai_list.begin();
+ while(ai_list_itr != ai_list.end()) {
+ if ((*ai_list_itr)->getDie()) {
+ tmgr->release((*ai_list_itr)->getID());
+ --mNumAiModels;
+ (*ai_list_itr)->unbind();
+ ai_list_itr = ai_list.erase(ai_list_itr);
+ } else {
+ fetchUserState();
+ if ((*ai_list_itr)->isa(FGAIBase::otThermal)) {
+ FGAIBase *base = *ai_list_itr;
+ processThermal((FGAIThermal*)base);
+ } else {
+ (*ai_list_itr)->update(_dt);
+ }
+ ++ai_list_itr;
+ }
+ }
+ wind_from_down_node->setDoubleValue( strength ); // for thermals
}
void
{
model->setManager(this);
ai_list.push_back(model);
- ++numObjects[0];
- ++numObjects[model->getType()];
+ ++mNumAiModels;
model->init();
model->bind();
}
void FGAIManager::destroyObject( int ID ) {
- ai_list_iterator ai_list_itr = ai_list.begin();
- while(ai_list_itr != ai_list.end()) {
- if ((*ai_list_itr)->getID() == ID) {
- --numObjects[0];
- --numObjects[(*ai_list_itr)->getType()];
- (*ai_list_itr)->unbind();
- ai_list.erase(ai_list_itr);
- break;
- }
- ++ai_list_itr;
- }
+ ai_list_iterator ai_list_itr = ai_list.begin();
+ while(ai_list_itr != ai_list.end()) {
+ if ((*ai_list_itr)->getID() == ID) {
+ --mNumAiModels;
+ (*ai_list_itr)->unbind();
+ ai_list_itr = ai_list.erase(ai_list_itr);
+ } else
+ ++ai_list_itr;
+ }
}
+int
+FGAIManager::getNumAiObjects(void) const
+{
+ return mNumAiModels;
+}
void FGAIManager::fetchUserState( void ) {
user_latitude = user_latitude_node->getDoubleValue();
user_speed = user_speed_node->getDoubleValue() * 0.592484;
wind_from_east = wind_from_east_node->getDoubleValue();
wind_from_north = wind_from_north_node->getDoubleValue();
-
-
}
inline double get_wind_from_east() const {return wind_from_east; }
inline double get_wind_from_north() const {return wind_from_north; }
- inline int getNum( FGAIBase::object_type ot ) const {
- return (0 < ot && ot < FGAIBase::MAX_OBJECTS) ? numObjects[ot] : numObjects[0];
- }
+ int getNumAiObjects(void) const;
void processScenario( const string &filename );
private:
bool enabled;
- int numObjects[FGAIBase::MAX_OBJECTS];
+ int mNumAiModels;
+
SGPropertyNode* root;
SGPropertyNode* wind_from_down_node;
SGPropertyNode* user_latitude_node;
// 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
# 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;
+// #define SG_DEBUG SG_ALERT
FGAIMultiplayer::FGAIMultiplayer() : FGAIBase(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;
+ no_roll = false;
+
+ mTimeOffsetSet = false;
+ mAllowExtrapolation = true;
+ mLagAdjustSystemSpeed = 10;
}
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());
+#define AIMPROProp(type, name) \
+SGRawValueMethods<FGAIMultiplayer, type>(*this, &FGAIMultiplayer::get##name)
+
+#define AIMPRWProp(type, name) \
+SGRawValueMethods<FGAIMultiplayer, type>(*this, \
+ &FGAIMultiplayer::get##name, &FGAIMultiplayer::set##name)
+ props->tie("callsign", AIMPROProp(const char *, CallSign));
+
+ props->tie("controls/allow-extrapolation",
+ AIMPRWProp(bool, AllowExtrapolation));
+ props->tie("controls/lag-adjust-system-speed",
+ AIMPRWProp(double, LagAdjustSystemSpeed));
+
+#undef AIMPROProp
+#undef AIMPRWProp
}
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();
+ props->untie("callsign");
+ props->untie("controls/allow-extrapolation");
+ props->untie("controls/lag-adjust-system-speed");
}
-
-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;
+void FGAIMultiplayer::update(double dt)
+{
+ if (dt <= 0)
+ return;
+
+ FGAIBase::update(dt);
+
+ // Check if we already got data
+ if (mMotionInfo.empty())
+ return;
+
+ // The current simulation time we need to update for,
+ // note that the simulation time is updated before calling all the
+ // update methods. Thus it contains the time intervals *end* time
+ double curtime = globals->get_sim_time_sec();
+
+ // Get the last available time
+ MotionInfo::reverse_iterator it = mMotionInfo.rbegin();
+ double curentPkgTime = it->second.time;
+
+ // Dynamically optimize the time offset between the feeder and the client
+ // Well, 'dynamically' means that the dynamic of that update must be very
+ // slow. You would otherwise notice huge jumps in the multiplayer models.
+ // The reason is that we want to avoid huge extrapolation times since
+ // extrapolation is highly error prone. For that we need something
+ // approaching the average latency of the packets. This first order lag
+ // component will provide this. We just take the error of the currently
+ // requested time to the most recent available packet. This is the
+ // target we want to reach in average.
+ double lag = it->second.lag;
+ if (!mTimeOffsetSet) {
+ mTimeOffsetSet = true;
+ mTimeOffset = curentPkgTime - curtime - lag;
+ } else {
+ double offset = curentPkgTime - curtime - lag;
+ if (!mAllowExtrapolation && offset + lag < mTimeOffset) {
+ mTimeOffset = offset;
+ SG_LOG(SG_GENERAL, SG_DEBUG, "Resetting time offset adjust system to "
+ "avoid extrapolation: time offset = " << mTimeOffset);
+ } else {
+ // the error of the offset, respectively the negative error to avoid
+ // a minus later ...
+ double err = offset - mTimeOffset;
+ // limit errors leading to shorter lag values somehow, that is late
+ // arriving packets will pessimize the overall lag much more than
+ // early packets will shorten the overall lag
+ double sysSpeed;
+ if (err < 0) {
+ // Ok, we have some very late packets and nothing newer increase the
+ // lag by the given speedadjust
+ sysSpeed = mLagAdjustSystemSpeed*err;
+ } else {
+ // We have a too pessimistic display delay shorten that a small bit
+ sysSpeed = SGMiscd::min(0.1*err*err, 0.5);
+ }
+
+ // simple euler integration for that first order system including some
+ // overshooting guard to prevent to aggressive system speeds
+ // (stiff systems) to explode the systems state
+ double systemIncrement = dt*sysSpeed;
+ if (fabs(err) < fabs(systemIncrement))
+ systemIncrement = err;
+ mTimeOffset += systemIncrement;
+
+ SG_LOG(SG_GENERAL, SG_DEBUG, "Offset adjust system: time offset = "
+ << mTimeOffset << ", expected longitudinal position error due to "
+ " current adjustment of the offset: "
+ << fabs(norm(it->second.linearVel)*systemIncrement));
+ }
+ }
+
+
+ // Compute the time in the feeders time scale which fits the current time
+ // we need to
+ double tInterp = curtime + mTimeOffset;
+
+ SGVec3d ecPos;
+ SGQuatf ecOrient;
+ if (tInterp <= curentPkgTime) {
+ // Ok, we need a time prevous to the last available packet,
+ // that is good ...
+
+ // Find the first packet before the target time
+ MotionInfo::iterator nextIt = mMotionInfo.upper_bound(tInterp);
+ if (nextIt == mMotionInfo.begin()) {
+ SG_LOG(SG_GENERAL, SG_DEBUG, "Taking oldest packet!");
+ // We have no packet before the target time, just use the first one
+ MotionInfo::iterator firstIt = mMotionInfo.begin();
+ ecPos = firstIt->second.position;
+ ecOrient = firstIt->second.orientation;
+
+ std::vector<FGFloatPropertyData>::const_iterator firstPropIt;
+ std::vector<FGFloatPropertyData>::const_iterator firstPropItEnd;
+ firstPropIt = firstIt->second.properties.begin();
+ firstPropItEnd = firstIt->second.properties.end();
+ while (firstPropIt != firstPropItEnd) {
+ float val = firstPropIt->value;
+ PropertyMap::iterator pIt = mPropertyMap.find(firstPropIt->id);
+ if (pIt != mPropertyMap.end())
+ pIt->second->setFloatValue(val);
+ ++firstPropIt;
+ }
+
+ } else {
+ // Ok, we have really found something where our target time is in between
+ // do interpolation here
+ MotionInfo::iterator prevIt = nextIt;
+ --prevIt;
+
+ // Interpolation coefficient is between 0 and 1
+ double intervalStart = prevIt->second.time;
+ double intervalEnd = nextIt->second.time;
+ double intervalLen = intervalEnd - intervalStart;
+ double tau = (tInterp - intervalStart)/intervalLen;
+
+ SG_LOG(SG_GENERAL, SG_DEBUG, "Multiplayer vehicle interpolation: ["
+ << intervalStart << ", " << intervalEnd << "], intervalLen = "
+ << intervalLen << ", interpolation parameter = " << tau);
+
+ // Here we do just linear interpolation on the position
+ ecPos = ((1-tau)*prevIt->second.position + tau*nextIt->second.position);
+ ecOrient = interpolate((float)tau, prevIt->second.orientation,
+ nextIt->second.orientation);
+
+ if (prevIt->second.properties.size()
+ == nextIt->second.properties.size()) {
+ std::vector<FGFloatPropertyData>::const_iterator prevPropIt;
+ std::vector<FGFloatPropertyData>::const_iterator prevPropItEnd;
+ std::vector<FGFloatPropertyData>::const_iterator nextPropIt;
+ std::vector<FGFloatPropertyData>::const_iterator nextPropItEnd;
+ prevPropIt = prevIt->second.properties.begin();
+ prevPropItEnd = prevIt->second.properties.end();
+ nextPropIt = nextIt->second.properties.begin();
+ nextPropItEnd = nextIt->second.properties.end();
+ while (prevPropIt != prevPropItEnd) {
+ float val = (1-tau)*prevPropIt->value + tau*nextPropIt->value;
+ PropertyMap::iterator pIt = mPropertyMap.find(prevPropIt->id);
+ if (pIt != mPropertyMap.end())
+ pIt->second->setFloatValue(val);
+ ++prevPropIt;
+ ++nextPropIt;
+ }
+ }
+
+ // Now throw away too old data
+ if (prevIt != mMotionInfo.begin()) {
+ --prevIt;
+ mMotionInfo.erase(mMotionInfo.begin(), prevIt);
+ }
}
-
- 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;
+ } else {
+ // Ok, we need to predict the future, so, take the best data we can have
+ // and do some eom computation to guess that for now.
+ FGExternalMotionData motionInfo = it->second;
+
+ // The time to predict, limit to 5 seconds
+ double t = tInterp - motionInfo.time;
+ t = SGMisc<double>::min(t, 5);
+
+ SG_LOG(SG_GENERAL, SG_DEBUG, "Multiplayer vehicle extrapolation: "
+ "extrapolation time = " << t);
+
+ // Do a few explicit euler steps with the constant acceleration's
+ // This must be sufficient ...
+ ecPos = motionInfo.position;
+ ecOrient = motionInfo.orientation;
+ SGVec3f linearVel = motionInfo.linearVel;
+ SGVec3f angularVel = motionInfo.angularVel;
+ while (0 < t) {
+ double h = 1e-1;
+ if (t < h)
+ h = t;
+
+ SGVec3d ecVel = toVec3d(ecOrient.backTransform(linearVel));
+ ecPos += h*ecVel;
+ ecOrient += h*ecOrient.derivative(angularVel);
+
+ linearVel += h*(cross(linearVel, angularVel) + motionInfo.linearAccel);
+ angularVel += h*motionInfo.angularAccel;
+
+ t -= h;
}
- 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;
+ std::vector<FGFloatPropertyData>::const_iterator firstPropIt;
+ std::vector<FGFloatPropertyData>::const_iterator firstPropItEnd;
+ firstPropIt = it->second.properties.begin();
+ firstPropItEnd = it->second.properties.end();
+ while (firstPropIt != firstPropItEnd) {
+ float val = firstPropIt->value;
+ PropertyMap::iterator pIt = mPropertyMap.find(firstPropIt->id);
+ if (pIt != mPropertyMap.end())
+ pIt->second->setFloatValue(val);
+ ++firstPropIt;
}
-
- // 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);
+ }
+
+ // extract the position
+ SGGeod geod = ecPos;
+ pos.setlat(geod.getLatitudeDeg());
+ pos.setlon(geod.getLongitudeDeg());
+ pos.setelev(geod.getElevationM());
+
+ // The quaternion rotating from the earth centered frame to the
+ // horizontal local frame
+ SGQuatf qEc2Hl = SGQuatf::fromLonLat((float)geod.getLongitudeRad(),
+ (float)geod.getLatitudeRad());
+ // The orientation wrt the horizontal local frame
+ SGQuatf hlOr = conj(qEc2Hl)*ecOrient;
+ float hDeg, pDeg, rDeg;
+ hlOr.getEulerDeg(hDeg, pDeg, rDeg);
+ hdg = hDeg;
+ roll = rDeg;
+ pitch = pDeg;
+
+ SG_LOG(SG_GENERAL, SG_DEBUG, "Multiplayer position and orientation: "
+ << geod << ", " << hlOr);
+
+ //###########################//
+ // do calculations for radar //
+ //###########################//
+ UpdateRadar(manager);
+
+ Transform();
+}
+
+void
+FGAIMultiplayer::addMotionInfo(const FGExternalMotionData& motionInfo,
+ long stamp)
+{
+ mLastTimestamp = stamp;
+ // Drop packets arriving out of order
+ if (!mMotionInfo.empty() && motionInfo.time < mMotionInfo.rbegin()->first)
+ return;
+ mMotionInfo[motionInfo.time] = motionInfo;
}
-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;
+void
+FGAIMultiplayer::setDoubleProperty(const std::string& prop, double val)
+{
+ SGPropertyNode* pNode = props->getChild(prop.c_str(), true);
+ pNode->setDoubleValue(val);
}
// 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
//
#ifndef _FG_AIMultiplayer_HXX
#define _FG_AIMultiplayer_HXX
-#include "AIManager.hxx"
-#include "AIBase.hxx"
-
-//#include <Traffic/SchedFlight.hxx>
-//#include <Traffic/Schedule.hxx>
-
+#include <map>
#include <string>
-SG_USING_STD(string);
+#include <MultiPlayer/mpmessages.hxx>
+#include "AIBase.hxx"
class FGAIMultiplayer : public FGAIBase {
+public:
+ FGAIMultiplayer();
+ virtual ~FGAIMultiplayer();
+
+ virtual bool init();
+ virtual void bind();
+ virtual void unbind();
+ virtual void update(double dt);
+
+ void addMotionInfo(const FGExternalMotionData& motionInfo, long stamp);
+ void setDoubleProperty(const std::string& prop, double val);
+
+ void setCallSign(const string& callSign)
+ { mCallSign = callSign; }
+ const char* getCallSign(void) const
+ { return mCallSign.c_str(); }
+
+ long getLastTimestamp(void) const
+ { return mLastTimestamp; }
- public:
- FGAIMultiplayer();
- ~FGAIMultiplayer();
-
- bool init();
- virtual void bind();
- virtual void unbind();
- void update(double dt);
-
+ void setAllowExtrapolation(bool allowExtrapolation)
+ { mAllowExtrapolation = allowExtrapolation; }
+ bool getAllowExtrapolation(void) const
+ { return mAllowExtrapolation; }
+ void setLagAdjustSystemSpeed(double lagAdjustSystemSpeed)
+ {
+ if (lagAdjustSystemSpeed < 0)
+ lagAdjustSystemSpeed = 0;
+ mLagAdjustSystemSpeed = lagAdjustSystemSpeed;
+ }
+ double getLagAdjustSystemSpeed(void) const
+ { return mLagAdjustSystemSpeed; }
- 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);
+ void addPropertyId(unsigned id, const char* name)
+ { mPropertyMap[id] = props->getNode(name, true); }
- virtual const char* getTypeString(void) const { return "multiplayer"; }
+ virtual const char* getTypeString(void) const { return "multiplayer"; }
- 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;
+private:
- SGPropertyNode_ptr _time_node;
-
- void Run(double dt);
- inline double sign(double x) { return (x < 0.0) ? -1.0 : 1.0; }
+ // Automatic sorting of motion data according to its timestamp
+ typedef std::map<double,FGExternalMotionData> MotionInfo;
+ MotionInfo mMotionInfo;
- string acType;
- string company;
-};
+ // Map between the property id's from the multiplayers network packets
+ // and the property nodes
+ typedef std::map<unsigned, SGSharedPtr<SGPropertyNode> > PropertyMap;
+ PropertyMap mPropertyMap;
+
+ std::string mCallSign;
+
+ double mTimeOffset;
+ bool mTimeOffsetSet;
+
+ /// Properties which are for now exposed for testing
+ bool mAllowExtrapolation;
+ double mLagAdjustSystemSpeed;
-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; }
+ long mLastTimestamp;
+};
#endif // _FG_AIMultiplayer_HXX
EXTRA_DIST = 3dfx.sh runfgfs.in runfgfs.bat.in \
fg_os_sdl.cxx fg_os.cxx fg_os.hxx
-if ENABLE_MPLAYER_AS
MPLAYER_LIBS = $(top_builddir)/src/MultiPlayer/libMultiPlayer.a
-else
-MPLAYER_LIBS =
-endif
if ENABLE_SP_FDM
SP_FDM_LIBS = $(top_builddir)/src/FDM/SP/libSPFDM.a
#include <Time/sunsolver.hxx>
#include <Time/tmp.hxx>
#include <Traffic/TrafficMgr.hxx>
-
-#ifdef FG_MPLAYER_AS
#include <MultiPlayer/multiplaymgr.hxx>
-#endif
#include <Environment/environment_mgr.hxx>
globals->get_subsystem_mgr()->bind();
globals->get_subsystem_mgr()->init();
-#ifdef FG_MPLAYER_AS
////////////////////////////////////////////////////////////////////
// Initialize multiplayer subsystem
////////////////////////////////////////////////////////////////////
globals->set_multiplayer_mgr(new FGMultiplayMgr);
globals->get_multiplayer_mgr()->init();
-#endif
////////////////////////////////////////////////////////////////////////
// Initialize the Nasal interpreter.
#include <Network/ray.hxx>
#include <Network/rul.hxx>
#include <Network/generic.hxx>
-
-#ifdef FG_MPLAYER_AS
#include <Network/multiplay.hxx>
-#endif
#include "globals.hxx"
#include "fg_io.hxx"
else if (tokens[1] == "file") n--;
FGGeneric *generic = new FGGeneric( tokens[n] );
io = generic;
-
-#ifdef FG_MPLAYER_AS
} else if ( protocol == "multiplay" ) {\
//Determine dir, rate, host & port
string dir = tokens[1];
string host = tokens[3];
string port = tokens[4];
return new FGMultiplay(dir, atoi(rate.c_str()), host, atoi(port.c_str()));
-#endif
-
} else {
return NULL;
}
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;
}
class FGModelMgr;
class FGRouteMgr;
class FGScenery;
-#ifdef FG_MPLAYER_AS
class FGMultiplayMgr;
-#endif
class FGPanel;
class FGTileMgr;
class FGViewMgr;
FGTACANList *channellist;
FGFixList *fixlist;
-
-#ifdef FG_MPLAYER_AS
//Mulitplayer managers
FGMultiplayMgr *multiplayer_mgr;
-#endif
public:
model_mgr = mgr;
}
-#ifdef FG_MPLAYER_AS
inline FGMultiplayMgr *get_multiplayer_mgr () { return multiplayer_mgr; }
inline void set_multiplayer_mgr (FGMultiplayMgr * mgr)
multiplayer_mgr = mgr;
}
-#endif
-
inline string_list *get_channel_options_list () {
return channel_options_list;
}
#include <Time/fg_timer.hxx>
#include <Environment/environment_mgr.hxx>
#include <GUI/new_gui.hxx>
-
-#ifdef FG_MPLAYER_AS
#include <MultiPlayer/multiplaymgr.hxx>
-#endif
-
-
#include "fg_commands.hxx"
#include "fg_io.hxx"
++frames;
#endif
+ // Update any multiplayer's network queues, the AIMultiplayer
+ // implementation is an AI model and depends on that
+ globals->get_multiplayer_mgr()->Update();
+
// Run ATC subsystem
if (fgGetBool("/sim/atc/enabled"))
globals->get_ATC_mgr()->update(delta_time_sec);
// Run the AI subsystem
+ // FIXME: run that also if we have multiplying enabled since the
+ // multiplayer information is interpreted by an AI model
if (fgGetBool("/sim/ai-traffic/enabled"))
globals->get_AI_mgr()->update(delta_time_sec);
-#ifdef FG_MPLAYER_AS
- // Update any multiplayer models
- globals->get_multiplayer_mgr()->Update();
-#endif
-
// Run flight model
// Calculate model iterations needed for next frame
fgSetBool("/sim/freeze/clock", false);
fgSetBool("/sim/freeze/fuel", false);
-#ifdef FG_MPLAYER_AS
fgSetString("/sim/multiplay/callsign", "callsign");
fgSetString("/sim/multiplay/rxhost", "0");
fgSetString("/sim/multiplay/txhost", "0");
fgSetInt("/sim/multiplay/rxport", 0);
fgSetInt("/sim/multiplay/txport", 0);
-#endif
-
}
static bool
{"joyclient", true, OPTION_CHANNEL, "", false, "", 0 },
{"jsclient", true, OPTION_CHANNEL, "", false, "", 0 },
{"proxy", true, OPTION_FUNC, "", false, "", fgSetupProxy },
-#ifdef FG_MPLAYER_AS
{"callsign", true, OPTION_STRING, "sim/multiplay/callsign", false, "", 0 },
{"multiplay", true, OPTION_CHANNEL, "", false, "", 0 },
-#endif
{"trace-read", true, OPTION_FUNC, "", false, "", fgOptTraceRead },
{"trace-write", true, OPTION_FUNC, "", false, "", fgOptTraceWrite },
{"log-level", true, OPTION_FUNC, "", false, "", fgOptLogLevel },
globals->get_scenery()->unregister_placement_transform(_aircraft->getTransform());
delete _aircraft;
- delete _scene;
// SSG will delete it
globals->get_scenery()->get_aircraft_branch()->removeKid(_selector);
}
noinst_LIBRARIES = libMultiPlayer.a
-libMultiPlayer_a_SOURCES = multiplaymgr.cxx multiplaymgr.hxx mpplayer.cxx mpplayer.hxx mpmessages.hxx tiny_xdr.cxx tiny_xdr.hxx
+libMultiPlayer_a_SOURCES = multiplaymgr.cxx multiplaymgr.hxx mpmessages.hxx tiny_xdr.cxx tiny_xdr.hxx
INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src
// 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
*
******************************************************************/
+#include <vector>
+
#include <plib/sg.h>
#include <simgear/compiler.h>
+#include <simgear/math/SGMath.hxx>
#include "tiny_xdr.hxx"
// magic value for messages
// Message identifiers
#define CHAT_MSG_ID 1
#define UNUSABLE_POS_DATA_ID 2
-#define OLD_POS_DATA_ID 3
-#define POS_DATA_ID 4
-#define PROP_MSG_ID 5
+#define OLD_OLD_POS_DATA_ID 3
+#define OLD_POS_DATA_ID 4
+#define OLD_PROP_MSG_ID 5
+#define RESET_DATA_ID 6
+#define POS_DATA_ID 7
// XDR demands 4 byte alignment, but some compilers use8 byte alignment
// so it's safe to let the overall size of a network message be a
#define MAX_MODEL_NAME_LEN 96
#define MAX_PROPERTY_LEN 52
-/** Aircraft position message */
-typedef xdr_data2_t xdrPosition[3];
-typedef xdr_data_t xdrOrientation[4];
-
// Header for use with all messages sent
-class T_MsgHdr {
-public:
+struct T_MsgHdr {
xdr_data_t Magic; // Magic Value
xdr_data_t Version; // Protocoll version
xdr_data_t MsgId; // Message identifier
};
// Chat message
-class T_ChatMsg {
-public:
+struct T_ChatMsg {
char Text[MAX_CHAT_MSG_LEN]; // Text of chat message
};
// Position message
-class T_PositionMsg {
-public:
+struct T_PositionMsg {
char Model[MAX_MODEL_NAME_LEN]; // Name of the aircraft model
- 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
+
+ // Time when this packet was generated
+ xdr_data2_t time;
+ xdr_data2_t lag;
+
+ // position wrt the earth centered frame
+ xdr_data2_t position[3];
+ // orientation wrt the earth centered frame, stored in the angle axis
+ // representation where the angle is coded into the axis length
+ xdr_data_t orientation[3];
+
+ // linear velocity wrt the earth centered frame measured in
+ // the earth centered frame
+ xdr_data_t linearVel[3];
+ // angular velocity wrt the earth centered frame measured in
+ // the earth centered frame
+ xdr_data_t angularVel[3];
+
+ // linear acceleration wrt the earth centered frame measured in
+ // the earth centered frame
+ xdr_data_t linearAccel[3];
+ // angular acceleration wrt the earth centered frame measured in
+ // the earth centered frame
+ xdr_data_t angularAccel[3];
};
// 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
-
+struct T_PropertyMsg {
+ xdr_data_t id;
+ xdr_data_t value;
};
-#endif
+struct FGFloatPropertyData {
+ unsigned id;
+ float value;
+};
+// Position message
+struct FGExternalMotionData {
+ // simulation time when this packet was generated
+ double time;
+ // the artificial lag the client should stay behind the average
+ // simulation time to arrival time diference
+ // FIXME: should be some 'per model' instead of 'per packet' property
+ double lag;
+
+ // position wrt the earth centered frame
+ SGVec3d position;
+ // orientation wrt the earth centered frame
+ SGQuatf orientation;
+
+ // linear velocity wrt the earth centered frame measured in
+ // the earth centered frame
+ SGVec3f linearVel;
+ // angular velocity wrt the earth centered frame measured in
+ // the earth centered frame
+ SGVec3f angularVel;
+
+ // linear acceleration wrt the earth centered frame measured in
+ // the earth centered frame
+ SGVec3f linearAccel;
+ // angular acceleration wrt the earth centered frame measured in
+ // the earth centered frame
+ SGVec3f angularAccel;
+
+ // The set of properties recieved for this timeslot
+ std::vector<FGFloatPropertyData> properties;
+};
+#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
+// Copyright (C) 2006 Mathias Froehlich
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
#include <config.h>
#endif
-#ifdef FG_MPLAYER_AS
-
-#include <sys/types.h>
-#if !(defined(_MSC_VER) || defined(__MINGW32__))
-# include <sys/socket.h>
-# include <netinet/in.h>
-# include <arpa/inet.h>
-#endif
-#include <stdlib.h>
-
#include <plib/netSocket.h>
+#include <simgear/timing/timestamp.hxx>
#include <simgear/debug/logstream.hxx>
+#include <AIModel/AIManager.hxx>
#include <Main/fg_props.hxx>
+
#include "multiplaymgr.hxx"
#include "mpmessages.hxx"
-#include "mpplayer.hxx"
-#define MAX_PACKET_SIZE 1024
+#define MAX_PACKET_SIZE 1200
// These constants are provided so that the ident
// command can list file versions
-const char sMULTIPLAYMGR_BID[] =
- "$Id$";
+const char sMULTIPLAYMGR_BID[] = "$Id$";
const char sMULTIPLAYMGR_HID[] = MULTIPLAYTXMGR_HID;
+// A static map of protocol property id values to property paths,
+// This should be extendable dynamically for every specific aircraft ...
+// For now only that static list
+FGMultiplayMgr::IdPropertyList
+FGMultiplayMgr::sIdPropertyList[] = {
+ {100, "surface-positions/left-aileron-pos-norm"},
+ {101, "surface-positions/right-aileron-pos-norm"},
+ {102, "surface-positions/elevator-pos-norm"},
+ {103, "surface-positions/rudder-pos-norm"},
+ {104, "surface-positions/flap-pos-norm"},
+ {105, "surface-positions/speedbrake-pos-norm"},
+ {106, "gear/tailhook/position-norm"},
+
+ {200, "gear/gear[0]/compression-norm"},
+ {201, "gear/gear[0]/position-norm"},
+ {210, "gear/gear[1]/compression-norm"},
+ {211, "gear/gear[1]/position-norm"},
+ {220, "gear/gear[2]/compression-norm"},
+ {221, "gear/gear[2]/position-norm"},
+ {230, "gear/gear[3]/compression-norm"},
+ {231, "gear/gear[3]/position-norm"},
+ {240, "gear/gear[4]/compression-norm"},
+ {241, "gear/gear[4]/position-norm"},
+
+ {300, "engines/engine[0]/n1"},
+ {301, "engines/engine[0]/n2"},
+ {302, "engines/engine[0]/rpm"},
+ {310, "engines/engine[1]/n1"},
+ {311, "engines/engine[1]/n2"},
+ {312, "engines/engine[1]/rpm"},
+ {320, "engines/engine[2]/n1"},
+ {321, "engines/engine[2]/n2"},
+ {322, "engines/engine[2]/rpm"},
+ {330, "engines/engine[3]/n1"},
+ {331, "engines/engine[3]/n2"},
+ {332, "engines/engine[3]/rpm"},
+ {340, "engines/engine[4]/n1"},
+ {341, "engines/engine[4]/n2"},
+ {342, "engines/engine[4]/rpm"},
+ {350, "engines/engine[5]/n1"},
+ {351, "engines/engine[5]/n2"},
+ {352, "engines/engine[5]/rpm"},
+ {360, "engines/engine[6]/n1"},
+ {361, "engines/engine[6]/n2"},
+ {362, "engines/engine[6]/rpm"},
+ {370, "engines/engine[7]/n1"},
+ {371, "engines/engine[7]/n2"},
+ {372, "engines/engine[7]/rpm"},
+ {380, "engines/engine[8]/n1"},
+ {381, "engines/engine[8]/n2"},
+ {382, "engines/engine[8]/rpm"},
+ {390, "engines/engine[9]/n1"},
+ {391, "engines/engine[9]/n2"},
+ {392, "engines/engine[9]/rpm"},
+
+ {1001, "controls/flight/slats"},
+ {1002, "controls/flight/speedbrake"},
+ {1003, "controls/flight/spoilers"},
+ {1004, "controls/gear/gear-down"},
+ {1005, "controls/lighting/nav-lights"},
+
+ /// termination
+ {0, 0}
+};
+
//////////////////////////////////////////////////////////////////////
//
// MultiplayMgr constructor
//////////////////////////////////////////////////////////////////////
FGMultiplayMgr::FGMultiplayMgr()
{
- m_Initialised = false;
- m_LocalPlayer = NULL;
- m_RxAddress = "0";
- m_RxPort = 0;
- m_Initialised = false;
- m_HaveServer = false;
-
- send_all_props= false;
+ mInitialised = false;
+ mHaveServer = false;
} // FGMultiplayMgr::FGMultiplayMgr()
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
FGMultiplayMgr::~FGMultiplayMgr()
{
- Close();
+ Close();
} // 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
bool
FGMultiplayMgr::init (void)
{
- string TxAddress; // Destination address
- int TxPort;
-
- //////////////////////////////////////////////////
- // Initialise object if not already done
- //////////////////////////////////////////////////
- if (m_Initialised)
- {
- SG_LOG( SG_NETWORK, SG_WARN,
- "FGMultiplayMgr::init - already initialised" );
- return (false);
- }
- //////////////////////////////////////////////////
- // Set members from property values
- //////////////////////////////////////////////////
- TxAddress = fgGetString ("/sim/multiplay/txhost");
- TxPort = fgGetInt ("/sim/multiplay/txport");
- m_Callsign = fgGetString ("/sim/multiplay/callsign");
- m_RxAddress = fgGetString ("/sim/multiplay/rxhost");
- m_RxPort = fgGetInt ("/sim/multiplay/rxport");
- if (m_RxPort <= 0)
- {
- m_RxPort = 5000;
- }
- if (m_Callsign == "")
- {
- // FIXME: use getpwuid
- m_Callsign = "JohnDoe";
- }
- if (m_RxAddress == "")
- {
- m_RxAddress = "127.0.0.1";
- }
- if ((TxPort > 0) && (TxAddress != ""))
- {
- m_HaveServer = true;
- m_Server.set (TxAddress.c_str(), TxPort);
- }
- SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-txaddress= "<<TxAddress);
- SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-txport= "<<TxPort );
- SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-rxaddress="<<m_RxAddress );
- SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-rxport= "<<m_RxPort);
- SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-callsign= "<<m_Callsign);
- m_DataSocket = new netSocket();
- if (!m_DataSocket->open(false))
- {
- SG_LOG( SG_NETWORK, SG_ALERT,
- "FGMultiplayMgr::init - Failed to create data socket" );
- return (false);
- }
- m_DataSocket->setBlocking(false);
- m_DataSocket->setBroadcast(true);
- if (m_DataSocket->bind(m_RxAddress.c_str(), m_RxPort) != 0)
- {
- perror("bind");
- SG_LOG( SG_NETWORK, SG_ALERT,
- "FGMultiplayMgr::Open - Failed to bind receive socket" );
- return (false);
- }
- m_LocalPlayer = new MPPlayer();
- if (!m_LocalPlayer->Open(m_RxAddress, m_RxPort, m_Callsign,
- fgGetString("/sim/model/path"), true))
- {
- SG_LOG( SG_NETWORK, SG_ALERT,
- "FGMultiplayMgr::init - Failed to create local player" );
- return (false);
- }
- m_Initialised = true;
- send_all_props= true;
- return (true);
+ //////////////////////////////////////////////////
+ // Initialise object if not already done
+ //////////////////////////////////////////////////
+ if (mInitialised) {
+ SG_LOG(SG_NETWORK, SG_WARN, "FGMultiplayMgr::init - already initialised");
+ return false;
+ }
+ //////////////////////////////////////////////////
+ // Set members from property values
+ //////////////////////////////////////////////////
+ short rxPort = fgGetInt("/sim/multiplay/rxport");
+ if (rxPort <= 0)
+ rxPort = 5000;
+ mCallsign = fgGetString("/sim/multiplay/callsign");
+ if (mCallsign.empty())
+ // FIXME: use getpwuid
+ mCallsign = "JohnDoe";
+ string rxAddress = fgGetString("/sim/multiplay/rxhost");
+ if (rxAddress.empty())
+ rxAddress = "127.0.0.1";
+ short txPort = fgGetInt("/sim/multiplay/txport");
+ string txAddress = fgGetString("/sim/multiplay/txhost");
+ if (txPort > 0 && !txAddress.empty()) {
+ mHaveServer = true;
+ mServer.set(txAddress.c_str(), txPort);
+ }
+ SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-txaddress= "<<txAddress);
+ SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-txport= "<<txPort );
+ SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-rxaddress="<<rxAddress );
+ SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-rxport= "<<rxPort);
+ SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-callsign= "<<mCallsign);
+ mSocket = new netSocket();
+ if (!mSocket->open(false)) {
+ SG_LOG( SG_NETWORK, SG_ALERT,
+ "FGMultiplayMgr::init - Failed to create data socket" );
+ return false;
+ }
+ mSocket->setBlocking(false);
+ mSocket->setBroadcast(true);
+ if (mSocket->bind(rxAddress.c_str(), rxPort) != 0) {
+ perror("bind");
+ SG_LOG( SG_NETWORK, SG_ALERT,
+ "FGMultiplayMgr::Open - Failed to bind receive socket" );
+ return false;
+ }
+ mInitialised = true;
+ return true;
} // FGMultiplayMgr::init()
//////////////////////////////////////////////////////////////////////
void
FGMultiplayMgr::Close (void)
{
- //////////////////////////////////////////////////
- // Delete local player
- //////////////////////////////////////////////////
- if (m_LocalPlayer)
- {
- delete m_LocalPlayer;
- m_LocalPlayer = NULL;
- }
- //////////////////////////////////////////////////
- // Delete any existing players
- //////////////////////////////////////////////////
- t_MPClientListIterator CurrentPlayer;
- t_MPClientListIterator P;
- CurrentPlayer = m_MPClientList.begin ();
- while (CurrentPlayer != m_MPClientList.end ())
- {
- P = CurrentPlayer;
- delete (*P);
- *P = 0;
- CurrentPlayer = m_MPClientList.erase (P);
- }
- //////////////////////////////////////////////////
- // Delete socket
- //////////////////////////////////////////////////
- if (m_DataSocket)
- {
- m_DataSocket->close();
- delete m_DataSocket;
- m_DataSocket = NULL;
- }
- m_Initialised = false;
+ mMultiPlayerMap.clear();
+
+ if (mSocket) {
+ mSocket->close();
+ delete mSocket;
+ mSocket = 0;
+ }
+ mInitialised = false;
} // FGMultiplayMgr::Close(void)
//////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////
void
-FGMultiplayMgr::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
- )
+FGMultiplayMgr::SendMyPosition(const FGExternalMotionData& motionInfo)
{
- T_MsgHdr MsgHdr;
- T_PositionMsg PosMsg;
- char Msg[sizeof(T_MsgHdr) + sizeof(T_PositionMsg)];
-
- if ((! m_Initialised) || (! m_HaveServer))
- {
- if (! m_Initialised)
- SG_LOG( SG_NETWORK, SG_ALERT,
- "FGMultiplayMgr::SendMyPosition - not initialised" );
- if (! m_HaveServer)
- SG_LOG( SG_NETWORK, SG_ALERT,
- "FGMultiplayMgr::SendMyPosition - no server" );
- return;
- }
- 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" );
-
+ if ((! mInitialised) || (! mHaveServer)) {
+ if (! mInitialised)
+ SG_LOG( SG_NETWORK, SG_ALERT,
+ "FGMultiplayMgr::SendMyPosition - not initialised" );
+ if (! mHaveServer)
+ SG_LOG( SG_NETWORK, SG_ALERT,
+ "FGMultiplayMgr::SendMyPosition - no server" );
+ return;
+ }
+
+ T_PositionMsg PosMsg;
+ strncpy(PosMsg.Model, fgGetString("/sim/model/path"), MAX_MODEL_NAME_LEN);
+ PosMsg.Model[MAX_MODEL_NAME_LEN - 1] = '\0';
+
+ PosMsg.time = XDR_encode_double (motionInfo.time);
+ PosMsg.lag = XDR_encode_double (motionInfo.lag);
+ for (unsigned i = 0 ; i < 3; ++i)
+ PosMsg.position[i] = XDR_encode_double (motionInfo.position(i));
+ SGVec3f angleAxis;
+ motionInfo.orientation.getAngleAxis(angleAxis);
+ for (unsigned i = 0 ; i < 3; ++i)
+ PosMsg.orientation[i] = XDR_encode_float (angleAxis(i));
+ for (unsigned i = 0 ; i < 3; ++i)
+ PosMsg.linearVel[i] = XDR_encode_float (motionInfo.linearVel(i));
+ for (unsigned i = 0 ; i < 3; ++i)
+ PosMsg.angularVel[i] = XDR_encode_float (motionInfo.angularVel(i));
+ for (unsigned i = 0 ; i < 3; ++i)
+ PosMsg.linearAccel[i] = XDR_encode_float (motionInfo.linearAccel(i));
+ for (unsigned i = 0 ; i < 3; ++i)
+ PosMsg.angularAccel[i] = XDR_encode_float (motionInfo.angularAccel(i));
+
+ char Msg[MAX_PACKET_SIZE];
+ memcpy(Msg + sizeof(T_MsgHdr), &PosMsg, sizeof(T_PositionMsg));
+
+ char* ptr = Msg + sizeof(T_MsgHdr) + sizeof(T_PositionMsg);
+ std::vector<FGFloatPropertyData>::const_iterator it;
+ it = motionInfo.properties.begin();
+ while (it != motionInfo.properties.end()
+ && ptr < (Msg + MAX_PACKET_SIZE - sizeof(T_PropertyMsg))) {
+ T_PropertyMsg pMsg;
+ pMsg.id = XDR_encode_uint32(it->id);
+ pMsg.value = XDR_encode_float(it->value);
+ memcpy(ptr, &pMsg, sizeof(T_PropertyMsg));
+ ptr += sizeof(T_PropertyMsg);
+ ++it;
+ }
+
+ T_MsgHdr MsgHdr;
+ FillMsgHdr(&MsgHdr, POS_DATA_ID, ptr - Msg);
+ memcpy(Msg, &MsgHdr, sizeof(T_MsgHdr));
+
+ mSocket->sendto(Msg, ptr - Msg, 0, &mServer);
+ 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
//
//////////////////////////////////////////////////////////////////////
void
-FGMultiplayMgr::SendTextMessage
- (
- const string &MsgText
- ) const
+FGMultiplayMgr::SendTextMessage(const string &MsgText)
{
- T_MsgHdr MsgHdr;
- T_ChatMsg ChatMsg;
- unsigned int iNextBlockPosition = 0;
- char Msg[sizeof(T_MsgHdr) + sizeof(T_ChatMsg)];
-
- if ((! m_Initialised) || (! m_HaveServer))
- {
- return;
- }
- m_LocalPlayer->FillMsgHdr(&MsgHdr, CHAT_MSG_ID);
- //////////////////////////////////////////////////
- // Divide the text string into blocks that fit
- // in the message and send the blocks.
- //////////////////////////////////////////////////
- while (iNextBlockPosition < MsgText.length())
- {
- strncpy (ChatMsg.Text,
- MsgText.substr(iNextBlockPosition, MAX_CHAT_MSG_LEN - 1).c_str(),
- MAX_CHAT_MSG_LEN);
- ChatMsg.Text[MAX_CHAT_MSG_LEN - 1] = '\0';
- memcpy (Msg, &MsgHdr, sizeof(T_MsgHdr));
- memcpy (Msg + sizeof(T_MsgHdr), &ChatMsg, sizeof(T_ChatMsg));
- m_DataSocket->sendto (Msg,
- sizeof(T_MsgHdr) + sizeof(T_ChatMsg), 0, &m_Server);
- iNextBlockPosition += MAX_CHAT_MSG_LEN - 1;
- }
+ if (!mInitialised || !mHaveServer)
+ return;
+
+ T_MsgHdr MsgHdr;
+ FillMsgHdr(&MsgHdr, CHAT_MSG_ID);
+ //////////////////////////////////////////////////
+ // Divide the text string into blocks that fit
+ // in the message and send the blocks.
+ //////////////////////////////////////////////////
+ unsigned iNextBlockPosition = 0;
+ T_ChatMsg ChatMsg;
+ char Msg[sizeof(T_MsgHdr) + sizeof(T_ChatMsg)];
+ while (iNextBlockPosition < MsgText.length()) {
+ strncpy (ChatMsg.Text,
+ MsgText.substr(iNextBlockPosition, MAX_CHAT_MSG_LEN - 1).c_str(),
+ MAX_CHAT_MSG_LEN);
+ ChatMsg.Text[MAX_CHAT_MSG_LEN - 1] = '\0';
+ memcpy (Msg, &MsgHdr, sizeof(T_MsgHdr));
+ memcpy (Msg + sizeof(T_MsgHdr), &ChatMsg, sizeof(T_ChatMsg));
+ mSocket->sendto (Msg, sizeof(T_MsgHdr) + sizeof(T_ChatMsg), 0, &mServer);
+ iNextBlockPosition += MAX_CHAT_MSG_LEN - 1;
+ }
} // FGMultiplayMgr::SendTextMessage ()
//////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////
void
-FGMultiplayMgr::ProcessData (void)
+FGMultiplayMgr::Update(void)
{
- char Msg[MAX_PACKET_SIZE]; // Buffer for received message
- int Bytes; // Bytes received
- T_MsgHdr* MsgHdr; // Pointer to header in received data
- netAddress SenderAddress;
-
- if (! m_Initialised)
- {
- SG_LOG( SG_NETWORK, SG_ALERT,
- "FGMultiplayMgr::ProcessData - not initialised" );
- return;
- }
+ if (!mInitialised)
+ return;
+
+ /// Just for expiry
+ SGTimeStamp timestamper;
+ timestamper.stamp();
+ long stamp = timestamper.get_seconds();
+
+ //////////////////////////////////////////////////
+ // Read the receive socket and process any data
+ //////////////////////////////////////////////////
+ int bytes;
+ do {
+ char Msg[MAX_PACKET_SIZE];
+ //////////////////////////////////////////////////
+ // Although the recv call asks for
+ // MAX_PACKET_SIZE of data, the number of bytes
+ // returned will only be that of the next
+ // packet waiting to be processed.
+ //////////////////////////////////////////////////
+ netAddress SenderAddress;
+ bytes = mSocket->recvfrom(Msg, sizeof(Msg), 0, &SenderAddress);
//////////////////////////////////////////////////
- // Read the receive socket and process any data
+ // no Data received
//////////////////////////////////////////////////
- do {
- //////////////////////////////////////////////////
- // Although the recv call asks for
- // MAX_PACKET_SIZE of data, the number of bytes
- // returned will only be that of the next
- // packet waiting to be processed.
- //////////////////////////////////////////////////
- Bytes = m_DataSocket->recvfrom (Msg, MAX_PACKET_SIZE, 0,
- &SenderAddress);
- //////////////////////////////////////////////////
- // no Data received
- //////////////////////////////////////////////////
- if (Bytes <= 0)
- {
- if (errno != EAGAIN)
- {
- perror("FGMultiplayMgr::MP_ProcessData");
- }
- return;
- }
- if (Bytes <= (int)sizeof(MsgHdr))
- {
- SG_LOG( SG_NETWORK, SG_ALERT,
- "FGMultiplayMgr::MP_ProcessData - "
+ if (bytes <= 0) {
+ if (errno != EAGAIN)
+ perror("FGMultiplayMgr::MP_ProcessData");
+ break;
+ }
+ if (bytes <= sizeof(T_MsgHdr)) {
+ SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
<< "received message with insufficient data" );
- return;
- }
- //////////////////////////////////////////////////
- // Read header
- //////////////////////////////////////////////////
- MsgHdr = (T_MsgHdr *)Msg;
- MsgHdr->Magic = XDR_decode_uint32 (MsgHdr->Magic);
- MsgHdr->Version = XDR_decode_uint32 (MsgHdr->Version);
- MsgHdr->MsgId = XDR_decode_uint32 (MsgHdr->MsgId);
- MsgHdr->MsgLen = XDR_decode_uint32 (MsgHdr->MsgLen);
- MsgHdr->ReplyPort = XDR_decode_uint32 (MsgHdr->ReplyPort);
- if (MsgHdr->Magic != MSG_MAGIC)
- {
- SG_LOG( SG_NETWORK, SG_ALERT,
- "FGMultiplayMgr::MP_ProcessData - "
+ break;
+ }
+ //////////////////////////////////////////////////
+ // Read header
+ //////////////////////////////////////////////////
+ T_MsgHdr* MsgHdr = (T_MsgHdr *)Msg;
+ MsgHdr->Magic = XDR_decode_uint32 (MsgHdr->Magic);
+ MsgHdr->Version = XDR_decode_uint32 (MsgHdr->Version);
+ MsgHdr->MsgId = XDR_decode_uint32 (MsgHdr->MsgId);
+ MsgHdr->MsgLen = XDR_decode_uint32 (MsgHdr->MsgLen);
+ MsgHdr->ReplyPort = XDR_decode_uint32 (MsgHdr->ReplyPort);
+ if (MsgHdr->Magic != MSG_MAGIC) {
+ SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
<< "message has invalid magic number!" );
- }
- if (MsgHdr->Version != PROTO_VER)
- {
- SG_LOG( SG_NETWORK, SG_ALERT,
- "FGMultiplayMgr::MP_ProcessData - "
+ }
+ if (MsgHdr->Version != PROTO_VER) {
+ SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
<< "message has invalid protocoll number!" );
- }
- //////////////////////////////////////////////////
- // Process the player data unless we generated it
- //////////////////////////////////////////////////
- if (m_Callsign == MsgHdr->Callsign)
- {
- return;
- }
- //////////////////////////////////////////////////
- // Process messages
- //////////////////////////////////////////////////
- switch(MsgHdr->MsgId)
- {
- case CHAT_MSG_ID:
- ProcessChatMsg ((char*) & Msg, SenderAddress);
- break;
- 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 - "
- << "Unknown message Id received: "
- << MsgHdr->MsgId );
- break;
- } // switch
- } while (Bytes > 0);
+ }
+ if (MsgHdr->MsgLen != bytes) {
+ SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
+ << "message has invalid length!" );
+ }
+ //////////////////////////////////////////////////
+ // Process messages
+ //////////////////////////////////////////////////
+ switch (MsgHdr->MsgId) {
+ case CHAT_MSG_ID:
+ ProcessChatMsg(Msg, SenderAddress);
+ break;
+ case POS_DATA_ID:
+ ProcessPosMsg(Msg, SenderAddress, bytes, stamp);
+ break;
+ case UNUSABLE_POS_DATA_ID:
+ case OLD_OLD_POS_DATA_ID:
+ case OLD_PROP_MSG_ID:
+ case OLD_POS_DATA_ID:
+ break;
+ default:
+ SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
+ << "Unknown message Id received: " << MsgHdr->MsgId );
+ break;
+ }
+ } while (bytes > 0);
+
+ // check for expiry
+ MultiPlayerMap::iterator it = mMultiPlayerMap.begin();
+ while (it != mMultiPlayerMap.end()) {
+ if (it->second->getLastTimestamp() + 10 < stamp) {
+ std::string name = it->first;
+ it->second->setDie(true);
+ mMultiPlayerMap.erase(it);
+ it = mMultiPlayerMap.upper_bound(name);
+ } else
+ ++it;
+ }
} // FGMultiplayMgr::ProcessData(void)
//////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////
void
-FGMultiplayMgr::ProcessPosMsg
- (
- const char *Msg,
- netAddress & SenderAddress
- )
+FGMultiplayMgr::ProcessPosMsg(const char *Msg, netAddress & SenderAddress,
+ unsigned len, long stamp)
{
- T_PositionMsg* PosMsg; // Pointer to position message in received data
- T_MsgHdr* MsgHdr; // Pointer to header in received data
- bool ActivePlayer;
- 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))
- {
- 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));
- 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
- //////////////////////////////////////////////////
- for (CurrentPlayer = m_MPClientList.begin ();
- CurrentPlayer != m_MPClientList.end ();
- CurrentPlayer++)
- {
- if ((*CurrentPlayer)->CompareCallsign(MsgHdr->Callsign))
- {
- // 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 (...)
- if (ActivePlayer)
- { // nothing more to do
- return;
- }
- //////////////////////////////////////////////////
- // Player not active, so add as new player
- //////////////////////////////////////////////////
- MPPlayer* NewPlayer;
- NewPlayer = new MPPlayer;
- NewPlayer->Open(SenderAddress.getHost(), MsgHdr->ReplyPort,
- MsgHdr->Callsign, PosMsg->Model, false);
- 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;
-
+ T_MsgHdr* MsgHdr = (T_MsgHdr *)Msg;
+ 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;
+ }
+ T_PositionMsg* PosMsg = (T_PositionMsg *)(Msg + sizeof(T_MsgHdr));
+ FGExternalMotionData motionInfo;
+ motionInfo.time = XDR_decode_double(PosMsg->time);
+ motionInfo.lag = XDR_decode_double(PosMsg->lag);
+ for (unsigned i = 0; i < 3; ++i)
+ motionInfo.position(i) = XDR_decode_double(PosMsg->position[i]);
+ SGVec3f angleAxis;
+ for (unsigned i = 0; i < 3; ++i)
+ angleAxis(i) = XDR_decode_float(PosMsg->orientation[i]);
+ motionInfo.orientation = SGQuatf::fromAngleAxis(angleAxis);
+ for (unsigned i = 0; i < 3; ++i)
+ motionInfo.linearVel(i) = XDR_decode_float(PosMsg->linearVel[i]);
+ for (unsigned i = 0; i < 3; ++i)
+ motionInfo.angularVel(i) = XDR_decode_float(PosMsg->angularVel[i]);
+ for (unsigned i = 0; i < 3; ++i)
+ motionInfo.linearAccel(i) = XDR_decode_float(PosMsg->linearAccel[i]);
+ for (unsigned i = 0; i < 3; ++i)
+ motionInfo.angularAccel(i) = XDR_decode_float(PosMsg->angularAccel[i]);
+
+ T_PropertyMsg* PropMsg
+ = (T_PropertyMsg*)(Msg + sizeof(T_MsgHdr) + sizeof(T_PositionMsg));
+ while ((char*)PropMsg < Msg + len) {
+ FGFloatPropertyData pData;
+ pData.id = XDR_decode_uint32(PropMsg->id);
+ pData.value = XDR_decode_float(PropMsg->value);
+ motionInfo.properties.push_back(pData);
+ ++PropMsg;
+ }
+
+ FGAIMultiplayer* mp = getMultiplayer(MsgHdr->Callsign);
+ if (!mp)
+ mp = addMultiplayer(MsgHdr->Callsign, PosMsg->Model);
+ mp->addMotionInfo(motionInfo, stamp);
} // FGMultiplayMgr::ProcessPosMsg()
//////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////
void
-FGMultiplayMgr::ProcessChatMsg
- (
- const char *Msg,
- netAddress & SenderAddress
- )
+FGMultiplayMgr::ProcessChatMsg(const char *Msg, netAddress& SenderAddress)
{
- T_ChatMsg* ChatMsg; // Pointer to chat message in received data
- T_MsgHdr* MsgHdr; // Pointer to header in received data
-
- MsgHdr = (T_MsgHdr *)Msg;
- 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 << "]" << " " << MsgBuf << endl);
- delete [] MsgBuf;
+ T_MsgHdr* MsgHdr = (T_MsgHdr *)Msg;
+ 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[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';
+
+ T_ChatMsg* ChatMsg = (T_ChatMsg *)(Msg + sizeof(T_MsgHdr));
+ SG_LOG ( SG_NETWORK, SG_ALERT, "Chat [" << MsgHdr->Callsign << "]"
+ << " " << MsgBuf << endl);
} // FGMultiplayMgr::ProcessChatMsg ()
//////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////
-//
-// handle a property message
-//
-//////////////////////////////////////////////////////////////////////
-void FGMultiplayMgr::ProcessPropMsg ( const char *Msg, netAddress & SenderAddress )
+void
+FGMultiplayMgr::FillMsgHdr(T_MsgHdr *MsgHdr, int MsgId, unsigned _len)
{
- 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 (...)
+ uint32_t len;
+ switch (MsgId) {
+ case CHAT_MSG_ID:
+ len = sizeof(T_MsgHdr) + sizeof(T_ChatMsg);
+ break;
+ case POS_DATA_ID:
+ len = _len;
+ break;
+ default:
+ len = sizeof(T_MsgHdr);
+ break;
+ }
+ MsgHdr->Magic = XDR_encode_uint32(MSG_MAGIC);
+ MsgHdr->Version = XDR_encode_uint32(PROTO_VER);
+ MsgHdr->MsgId = XDR_encode_uint32(MsgId);
+ MsgHdr->MsgLen = XDR_encode_uint32(len);
+ MsgHdr->ReplyAddress = 0; // Are obsolete, keep them for the server for
+ MsgHdr->ReplyPort = 0; // now
+ strncpy(MsgHdr->Callsign, mCallsign.c_str(), MAX_CALLSIGN_LEN);
+ MsgHdr->Callsign[MAX_CALLSIGN_LEN - 1] = '\0';
}
-//////////////////////////////////////////////////////////////////////
-//
-// For each active player, tell the player object
-// to update its position on the scene. If a player object
-// returns status information indicating that it has not
-// had an update for some time then the player is deleted.
-//
-//////////////////////////////////////////////////////////////////////
-void
-FGMultiplayMgr::Update (void)
+FGAIMultiplayer*
+FGMultiplayMgr::addMultiplayer(const std::string& callsign,
+ const std::string& modelName)
{
- MPPlayer::TPlayerDataState ePlayerDataState;
- t_MPClientListIterator CurrentPlayer;
-
- CurrentPlayer = m_MPClientList.begin ();
- while (CurrentPlayer != m_MPClientList.end ())
- {
- ePlayerDataState = (*CurrentPlayer)->Draw();
- //////////////////////////////////////////////////
- // If the player has not received an update for
- // some time then assume that the player has quit.
- //////////////////////////////////////////////////
- if (ePlayerDataState == MPPlayer::PLAYER_DATA_EXPIRED)
- {
- SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::Update - "
- << "Deleting player from game. Callsign: "
- << (*CurrentPlayer)->Callsign() );
- t_MPClientListIterator P;
- P = CurrentPlayer;
- delete (*P);
- *P = 0;
- CurrentPlayer = m_MPClientList.erase (P);
- }
- else CurrentPlayer++;
+ if (0 < mMultiPlayerMap.count(callsign))
+ return mMultiPlayerMap[callsign];
+
+ FGAIMultiplayer* mp = new FGAIMultiplayer;
+ mp->setPath(modelName.c_str());
+ mp->setCallSign(callsign);
+ mMultiPlayerMap[callsign] = mp;
+
+ FGAIManager *aiMgr = (FGAIManager*)globals->get_subsystem("ai_model");
+ if (aiMgr) {
+ aiMgr->attach(mp);
+
+ /// FIXME: that must follow the attach ATM ...
+ unsigned i = 0;
+ while (sIdPropertyList[i].name) {
+ mp->addPropertyId(sIdPropertyList[i].id, sIdPropertyList[i].name);
+ ++i;
}
-} // FGMultiplayMgr::Update()
-//////////////////////////////////////////////////////////////////////
+ }
-#endif // FG_MPLAYER_AS
+ return mp;
+}
+FGAIMultiplayer*
+FGMultiplayMgr::getMultiplayer(const std::string& callsign)
+{
+ if (0 < mMultiPlayerMap.count(callsign))
+ return mMultiPlayerMap[callsign];
+ else
+ return 0;
+}
#define MULTIPLAYTXMGR_HID "$Id$"
-#include "mpplayer.hxx"
#include "mpmessages.hxx"
#ifdef HAVE_CONFIG_H
#include <plib/netSocket.h>
#include <Main/globals.hxx>
-// Maximum number of players that can exist at any time
-// FIXME: use a list<mplayer> instead
-#define MAX_PLAYERS 10
+#include <AIModel/AIMultiplayer.hxx>
+
+struct FGExternalMotionInfo;
class FGMultiplayMgr
{
public:
- FGMultiplayMgr();
- ~FGMultiplayMgr();
- bool init(void);
- void Close(void);
- // transmitter
- 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;
+ struct IdPropertyList {
+ unsigned id;
+ const char* name;
+ };
+ static IdPropertyList sIdPropertyList[];
+ FGMultiplayMgr();
+ ~FGMultiplayMgr();
+ bool init(void);
+ void Close(void);
+ // transmitter
+ void SendMyPosition(const FGExternalMotionData& motionInfo);
+ void SendTextMessage(const string &sMsgText);
+ void FillMsgHdr(T_MsgHdr *MsgHdr, int iMsgId, unsigned _len = 0u);
+
+ // receiver
+ void ProcessPosMsg(const char *Msg, netAddress & SenderAddress,
+ unsigned len, long stamp);
+ void ProcessChatMsg(const char *Msg, netAddress & SenderAddress);
+ void Update(void);
+
private:
- typedef vector<MPPlayer*> t_MPClientList;
- typedef t_MPClientList::iterator t_MPClientListIterator;
- MPPlayer* m_LocalPlayer;
- netSocket* m_DataSocket;
- netAddress m_Server;
- bool m_HaveServer;
- bool m_Initialised;
- t_MPClientList m_MPClientList;
- string m_RxAddress;
- int m_RxPort;
- string m_Callsign;
+ FGAIMultiplayer* addMultiplayer(const std::string& callsign,
+ const std::string& modelName);
+ FGAIMultiplayer* getMultiplayer(const std::string& callsign);
+
+ /// maps from the callsign string to the FGAIMultiplayer
+ typedef std::map<std::string, SGSharedPtr<FGAIMultiplayer> > MultiPlayerMap;
+ MultiPlayerMap mMultiPlayerMap;
+
+ netSocket* mSocket;
+ netAddress mServer;
+ bool mHaveServer;
+ bool mInitialised;
+ string mCallsign;
};
#endif
//
//////////////////////////////////////////////////////////////////////
+#include <string>
+
#include <plib/ul.h>
+#include <plib/netSocket.h>
#include "tiny_xdr.hxx"
return tmp.d;
}
-
JPEG_SERVER =
endif
-if ENABLE_MPLAYER_AS
MPLAYER_AS = multiplay.cxx multiplay.hxx
-else
-MPLAYER_AS =
-endif
libNetwork_a_SOURCES = \
protocol.cxx protocol.hxx \
#include <string>
#include <simgear/debug/logstream.hxx>
-#include <simgear/scene/model/placement.hxx>
-#include <simgear/scene/model/placementtrans.hxx>
+#include <simgear/math/SGMath.hxx>
-#include <Scenery/scenery.hxx>
+#include <FDM/flight.hxx>
+#include <MultiPlayer/mpmessages.hxx>
#include "multiplay.hxx"
******************************************************************/
FGMultiplay::FGMultiplay (const string &dir, const int rate, const string &host, const int port) {
- last_time = 0;
- last_speedN = last_speedE = last_speedD = 0;
- calcaccN = calcaccE = calcaccD = 0;
- set_hz(rate);
+ set_hz(rate);
- set_direction(dir);
+ set_direction(dir);
- if (get_direction() == SG_IO_IN) {
-
- fgSetInt("/sim/multiplay/rxport", port);
- fgSetString("/sim/multiplay/rxhost", host.c_str());
-
- } else if (get_direction() == SG_IO_OUT) {
+ if (get_direction() == SG_IO_IN) {
- fgSetInt("/sim/multiplay/txport", port);
- fgSetString("/sim/multiplay/txhost", host.c_str());
+ fgSetInt("/sim/multiplay/rxport", port);
+ fgSetString("/sim/multiplay/rxhost", host.c_str());
- }
+ } else if (get_direction() == SG_IO_OUT) {
- 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;
- }
+ fgSetInt("/sim/multiplay/txport", port);
+ fgSetString("/sim/multiplay/txhost", host.c_str());
- 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);
+ SGPropertyNode* root = globals->get_props();
+
+ /// Build up the id to property map
+ unsigned i = 0;
+ while (FGMultiplayMgr::sIdPropertyList[i].name) {
+ const char* name = FGMultiplayMgr::sIdPropertyList[i].name;
+ SGPropertyNode* pNode = root->getNode(name);
+ if (pNode)
+ mPropertyMap[FGMultiplayMgr::sIdPropertyList[i].id] = pNode;
+ ++i;
+ }
+
return is_enabled();
}
******************************************************************/
bool FGMultiplay::process() {
- if (get_direction() == SG_IO_IN) {
-
- globals->get_multiplayer_mgr()->ProcessData();
-
- } else if (get_direction() == SG_IO_OUT) {
-
- 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(
- 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);
+ if (get_direction() == SG_IO_OUT) {
+
+ // check if we have left initialization phase. That will not provide
+ // interresting data, also the freeze in simulation time hurts the
+ // multiplayer clients
+ double sim_time = globals->get_sim_time_sec();
+// if (sim_time < 20)
+// return true;
+
+ FGInterface *ifce = cur_fdm_state;
+
+ // put together a motion info struct, you will get that later
+ // from FGInterface directly ...
+ FGExternalMotionData motionInfo;
+
+ // The current simulation time we need to update for,
+ // note that the simulation time is updated before calling all the
+ // update methods. Thus it contains the time intervals *end* time.
+ // The FDM is already run, so the states belong to that time.
+ motionInfo.time = sim_time;
+
+ // The typical lag will be the reciprocal of the output frequency
+ double hz = get_hz();
+ if (hz != 0) // I guess we can test a double for exact zero in this case
+ motionInfo.lag = 1/get_hz();
+ else
+ motionInfo.lag = 0.1; //??
+
+ // These are for now converted from lat/lon/alt and euler angles.
+ // But this should change in FGInterface ...
+ double lon = ifce->get_Longitude();
+ double lat = ifce->get_Latitude();
+ // first the aprioriate structure for the geodetic one
+ SGGeod geod = SGGeod::fromRadFt(lon, lat, ifce->get_Altitude());
+ // Convert to cartesion coordinate
+ motionInfo.position = geod;
- // 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;
- }
+ // The quaternion rotating from the earth centered frame to the
+ // horizontal local frame
+ SGQuatf qEc2Hl = SGQuatf::fromLonLat((float)lon, (float)lat);
+ // The orientation wrt the horizontal local frame
+ float heading = ifce->get_Psi();
+ float pitch = ifce->get_Theta();
+ float roll = ifce->get_Phi();
+ SGQuatf hlOr = SGQuatf::fromYawPitchRoll(heading, pitch, roll);
+ // The orientation of the vehicle wrt the earth centered frame
+ motionInfo.orientation = qEc2Hl*hlOr;
+
+ if (!ifce->is_suspended()) {
+ // velocities
+ motionInfo.linearVel = SG_FEET_TO_METER*SGVec3f(ifce->get_U_body(),
+ ifce->get_V_body(),
+ ifce->get_W_body());
+ motionInfo.angularVel = SGVec3f(ifce->get_P_body(),
+ ifce->get_Q_body(),
+ ifce->get_R_body());
+
+ // accels, set that to zero for now.
+ // Angular accelerations are missing from the interface anyway,
+ // linear accelerations are screwed up at least for JSBSim.
+// motionInfo.linearAccel = SG_FEET_TO_METER*SGVec3f(ifce->get_U_dot_body(),
+// ifce->get_V_dot_body(),
+// ifce->get_W_dot_body());
+ motionInfo.linearAccel = SGVec3f::zeros();
+ motionInfo.angularAccel = SGVec3f::zeros();
+ } else {
+ // if the interface is suspendend, prevent the client from
+ // wild extrapolations
+ motionInfo.linearVel = SGVec3f::zeros();
+ motionInfo.angularVel = SGVec3f::zeros();
+ motionInfo.linearAccel = SGVec3f::zeros();
+ motionInfo.angularAccel = SGVec3f::zeros();
}
- // 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);
+ // now send the properties
+ PropertyMap::iterator it;
+ for (it = mPropertyMap.begin(); it != mPropertyMap.end(); ++it) {
+ FGFloatPropertyData pData;
+ pData.id = it->first;
+ pData.value = it->second->getFloatValue();
+ motionInfo.properties.push_back(pData);
}
- //cout << " send_all out " << s << endl;
+
+ FGMultiplayMgr* mpmgr = globals->get_multiplayer_mgr();
+ mpmgr->SendMyPosition(motionInfo);
}
- return true;
+ return true;
}
} else if (get_direction() == SG_IO_OUT) {
-// globals->get_multiplayer_mgr()->Close();
+ globals->get_multiplayer_mgr()->Close();
}
- 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
-
#include STL_STRING
+#include <simgear/props/props.hxx>
#include <simgear/scene/model/model.hxx>
#include <Main/globals.hxx>
*
******************************************************************/
-#include <simgear/props/props.hxx>
-
class FGMultiplay : public FGProtocol {
public:
/** 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;
+ // Map between the property id's from the multiplayers network packets
+ // and the property nodes
+ typedef std::map<unsigned, SGSharedPtr<SGPropertyNode> > PropertyMap;
+ PropertyMap mPropertyMap;
};
+
+
#endif // _FG_MULTIPLAY_HXX