From 95a7e177d734b24312df69239c9647e8e5b310da Mon Sep 17 00:00:00 2001 From: Anders Gidenstam Date: Thu, 2 Sep 2010 19:46:44 +0200 Subject: [PATCH 1/5] Restored line endings in src/AIModel/AIMultiplayer.cxx. Please check that the line endings are not messed up before mergeing. --- src/AIModel/AIMultiplayer.cxx | 1016 ++++++++++++++++----------------- 1 file changed, 508 insertions(+), 508 deletions(-) diff --git a/src/AIModel/AIMultiplayer.cxx b/src/AIModel/AIMultiplayer.cxx index bd5cc0a90..f268b1822 100644 --- a/src/AIModel/AIMultiplayer.cxx +++ b/src/AIModel/AIMultiplayer.cxx @@ -1,508 +1,508 @@ -// 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. -// -// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include - -#include "AIMultiplayer.hxx" - -#include - -// #define SG_DEBUG SG_ALERT - -FGAIMultiplayer::FGAIMultiplayer() : FGAIBase(otMultiplayer) { - no_roll = false; - - mTimeOffsetSet = false; - mAllowExtrapolation = true; - mLagAdjustSystemSpeed = 10; - - aip.getSceneGraph()->setNodeMask(~SG_NODEMASK_TERRAIN_BIT); - -} - - -FGAIMultiplayer::~FGAIMultiplayer() { -} - -bool FGAIMultiplayer::init(bool search_in_AI_path) { - props->setStringValue("sim/model/path", model_path.c_str()); - //refuel_node = fgGetNode("systems/refuel/contact", true); - isTanker = false; // do this until this property is - // passed over the net - - string str1 = _getCallsign(); - string str2 = "MOBIL"; - - string::size_type loc1= str1.find( str2, 0 ); - if ( (loc1 != string::npos && str2 != "") ){ - // cout << " string found " << str2 << " in " << str1 << endl; - isTanker = true; - // cout << "isTanker " << isTanker << " " << mCallSign <tie("refuel/contact", SGRawValuePointer(&contact)); - props->tie("tanker", SGRawValuePointer(&isTanker)); - - props->tie("controls/invisible", - SGRawValuePointer(&invisible)); - -#define AIMPROProp(type, name) \ -SGRawValueMethods(*this, &FGAIMultiplayer::get##name) - -#define AIMPRWProp(type, name) \ -SGRawValueMethods(*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("callsign"); - props->untie("controls/allow-extrapolation"); - props->untie("controls/lag-adjust-system-speed"); - props->untie("controls/invisible"); - props->untie("refuel/contact"); -} - -void FGAIMultiplayer::update(double dt) -{ - using namespace simgear; - - 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) - || (offset - 10 > 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; - speed = norm(firstIt->second.linearVel) * SG_METER_TO_NM * 3600.0; - - std::vector::const_iterator firstPropIt; - std::vector::const_iterator firstPropItEnd; - firstPropIt = firstIt->second.properties.begin(); - firstPropItEnd = firstIt->second.properties.end(); - while (firstPropIt != firstPropItEnd) { - //cout << " Setting property..." << (*firstPropIt)->id; - PropertyMap::iterator pIt = mPropertyMap.find((*firstPropIt)->id); - if (pIt != mPropertyMap.end()) - { - //cout << "Found " << pIt->second->getPath() << ":"; - switch ((*firstPropIt)->type) { - case props::INT: - case props::BOOL: - case props::LONG: - pIt->second->setIntValue((*firstPropIt)->int_value); - //cout << "Int: " << (*firstPropIt)->int_value << "\n"; - break; - case props::FLOAT: - case props::DOUBLE: - pIt->second->setFloatValue((*firstPropIt)->float_value); - //cout << "Flo: " << (*firstPropIt)->float_value << "\n"; - break; - case props::STRING: - case props::UNSPECIFIED: - pIt->second->setStringValue((*firstPropIt)->string_value); - //cout << "Str: " << (*firstPropIt)->string_value << "\n"; - break; - default: - // FIXME - currently defaults to float values - pIt->second->setFloatValue((*firstPropIt)->float_value); - //cout << "Unknown: " << (*firstPropIt)->float_value << "\n"; - break; - } - } - else - { - SG_LOG(SG_GENERAL, SG_DEBUG, "Unable to find property: " << (*firstPropIt)->id << "\n"); - } - ++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); - speed = norm((1-tau)*prevIt->second.linearVel - + tau*nextIt->second.linearVel) * SG_METER_TO_NM * 3600.0; - - if (prevIt->second.properties.size() - == nextIt->second.properties.size()) { - std::vector::const_iterator prevPropIt; - std::vector::const_iterator prevPropItEnd; - std::vector::const_iterator nextPropIt; - std::vector::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) { - PropertyMap::iterator pIt = mPropertyMap.find((*prevPropIt)->id); - //cout << " Setting property..." << (*prevPropIt)->id; - - if (pIt != mPropertyMap.end()) - { - //cout << "Found " << pIt->second->getPath() << ":"; - - int ival; - float val; - switch ((*prevPropIt)->type) { - case props::INT: - case props::BOOL: - case props::LONG: - ival = (int) (0.5+(1-tau)*((double) (*prevPropIt)->int_value) + - tau*((double) (*nextPropIt)->int_value)); - pIt->second->setIntValue(ival); - //cout << "Int: " << ival << "\n"; - break; - case props::FLOAT: - case props::DOUBLE: - val = (1-tau)*(*prevPropIt)->float_value + - tau*(*nextPropIt)->float_value; - //cout << "Flo: " << val << "\n"; - pIt->second->setFloatValue(val); - break; - case props::STRING: - case props::UNSPECIFIED: - //cout << "Str: " << (*nextPropIt)->string_value << "\n"; - pIt->second->setStringValue((*nextPropIt)->string_value); - break; - default: - // FIXME - currently defaults to float values - val = (1-tau)*(*prevPropIt)->float_value + - tau*(*nextPropIt)->float_value; - //cout << "Unk: " << val << "\n"; - pIt->second->setFloatValue(val); - break; - } - } - else - { - SG_LOG(SG_GENERAL, SG_DEBUG, "Unable to find property: " << (*prevPropIt)->id << "\n"); - } - - ++prevPropIt; - ++nextPropIt; - } - } - - // Now throw away too old data - if (prevIt != mMotionInfo.begin()) - { - --prevIt; - - MotionInfo::iterator delIt; - delIt = mMotionInfo.begin(); - - while (delIt != prevIt) - { - std::vector::const_iterator propIt; - std::vector::const_iterator propItEnd; - propIt = delIt->second.properties.begin(); - propItEnd = delIt->second.properties.end(); - - //cout << "Deleting data\n"; - - while (propIt != propItEnd) - { - delete *propIt; - propIt++; - } - - delIt++; - } - - mMotionInfo.erase(mMotionInfo.begin(), prevIt); - } - } - } 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::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; - } - - std::vector::const_iterator firstPropIt; - std::vector::const_iterator firstPropItEnd; - speed = norm(linearVel) * SG_METER_TO_NM * 3600.0; - firstPropIt = it->second.properties.begin(); - firstPropItEnd = it->second.properties.end(); - while (firstPropIt != firstPropItEnd) { - PropertyMap::iterator pIt = mPropertyMap.find((*firstPropIt)->id); - //cout << " Setting property..." << (*firstPropIt)->id; - - if (pIt != mPropertyMap.end()) - { - switch ((*firstPropIt)->type) { - case props::INT: - case props::BOOL: - case props::LONG: - pIt->second->setIntValue((*firstPropIt)->int_value); - //cout << "Int: " << (*firstPropIt)->int_value << "\n"; - break; - case props::FLOAT: - case props::DOUBLE: - pIt->second->setFloatValue((*firstPropIt)->float_value); - //cout << "Flo: " << (*firstPropIt)->float_value << "\n"; - break; - case props::STRING: - case props::UNSPECIFIED: - pIt->second->setStringValue((*firstPropIt)->string_value); - //cout << "Str: " << (*firstPropIt)->string_value << "\n"; - break; - default: - // FIXME - currently defaults to float values - pIt->second->setFloatValue((*firstPropIt)->float_value); - //cout << "Unk: " << (*firstPropIt)->float_value << "\n"; - break; - } - } - else - { - SG_LOG(SG_GENERAL, SG_DEBUG, "Unable to find property: " << (*firstPropIt)->id << "\n"); - } - - ++firstPropIt; - } - } - - // extract the position - pos = SGGeod::fromCart(ecPos); - altitude_ft = pos.getElevationFt(); - - // The quaternion rotating from the earth centered frame to the - // horizontal local frame - SGQuatf qEc2Hl = SGQuatf::fromLonLatRad((float)pos.getLongitudeRad(), - (float)pos.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: " - << ecPos << ", " << hlOr); - - //###########################// - // do calculations for radar // - //###########################// - double range_ft2 = UpdateRadar(manager); - - //************************************// - // Tanker code // - //************************************// - - - if ( isTanker) { - //cout << "IS tanker "; - if ( (range_ft2 < 250.0 * 250.0) && - (y_shift > 0.0) && - (elevation > 0.0) ){ - // refuel_node->setBoolValue(true); - //cout << "in contact" << endl; - contact = true; - } else { - // refuel_node->setBoolValue(false); - //cout << "not in contact" << endl; - contact = false; - } - } else { - //cout << "NOT tanker " << endl; - contact = false; - } - - Transform(); -} - -void -FGAIMultiplayer::addMotionInfo(const FGExternalMotionData& motionInfo, - long stamp) -{ - mLastTimestamp = stamp; - - if (!mMotionInfo.empty()) { - double diff = motionInfo.time - mMotionInfo.rbegin()->first; - - // packet is very old -- MP has probably reset (incl. his timebase) - if (diff < -10.0) - mMotionInfo.clear(); - - // drop packets arriving out of order - else if (diff < 0.0) - return; - } - mMotionInfo[motionInfo.time] = motionInfo; -} - -void -FGAIMultiplayer::setDoubleProperty(const std::string& prop, double val) -{ - SGPropertyNode* pNode = props->getChild(prop.c_str(), true); - pNode->setDoubleValue(val); -} +// 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. +// +// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "AIMultiplayer.hxx" + +#include + +// #define SG_DEBUG SG_ALERT + +FGAIMultiplayer::FGAIMultiplayer() : FGAIBase(otMultiplayer) { + no_roll = false; + + mTimeOffsetSet = false; + mAllowExtrapolation = true; + mLagAdjustSystemSpeed = 10; + + aip.getSceneGraph()->setNodeMask(~SG_NODEMASK_TERRAIN_BIT); + +} + + +FGAIMultiplayer::~FGAIMultiplayer() { +} + +bool FGAIMultiplayer::init(bool search_in_AI_path) { + props->setStringValue("sim/model/path", model_path.c_str()); + //refuel_node = fgGetNode("systems/refuel/contact", true); + isTanker = false; // do this until this property is + // passed over the net + + string str1 = _getCallsign(); + string str2 = "MOBIL"; + + string::size_type loc1= str1.find( str2, 0 ); + if ( (loc1 != string::npos && str2 != "") ){ + // cout << " string found " << str2 << " in " << str1 << endl; + isTanker = true; + // cout << "isTanker " << isTanker << " " << mCallSign <tie("refuel/contact", SGRawValuePointer(&contact)); + props->tie("tanker", SGRawValuePointer(&isTanker)); + + props->tie("controls/invisible", + SGRawValuePointer(&invisible)); + +#define AIMPROProp(type, name) \ +SGRawValueMethods(*this, &FGAIMultiplayer::get##name) + +#define AIMPRWProp(type, name) \ +SGRawValueMethods(*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("callsign"); + props->untie("controls/allow-extrapolation"); + props->untie("controls/lag-adjust-system-speed"); + props->untie("controls/invisible"); + props->untie("refuel/contact"); +} + +void FGAIMultiplayer::update(double dt) +{ + using namespace simgear; + + 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) + || (offset - 10 > 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; + speed = norm(firstIt->second.linearVel) * SG_METER_TO_NM * 3600.0; + + std::vector::const_iterator firstPropIt; + std::vector::const_iterator firstPropItEnd; + firstPropIt = firstIt->second.properties.begin(); + firstPropItEnd = firstIt->second.properties.end(); + while (firstPropIt != firstPropItEnd) { + //cout << " Setting property..." << (*firstPropIt)->id; + PropertyMap::iterator pIt = mPropertyMap.find((*firstPropIt)->id); + if (pIt != mPropertyMap.end()) + { + //cout << "Found " << pIt->second->getPath() << ":"; + switch ((*firstPropIt)->type) { + case props::INT: + case props::BOOL: + case props::LONG: + pIt->second->setIntValue((*firstPropIt)->int_value); + //cout << "Int: " << (*firstPropIt)->int_value << "\n"; + break; + case props::FLOAT: + case props::DOUBLE: + pIt->second->setFloatValue((*firstPropIt)->float_value); + //cout << "Flo: " << (*firstPropIt)->float_value << "\n"; + break; + case props::STRING: + case props::UNSPECIFIED: + pIt->second->setStringValue((*firstPropIt)->string_value); + //cout << "Str: " << (*firstPropIt)->string_value << "\n"; + break; + default: + // FIXME - currently defaults to float values + pIt->second->setFloatValue((*firstPropIt)->float_value); + //cout << "Unknown: " << (*firstPropIt)->float_value << "\n"; + break; + } + } + else + { + SG_LOG(SG_GENERAL, SG_DEBUG, "Unable to find property: " << (*firstPropIt)->id << "\n"); + } + ++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); + speed = norm((1-tau)*prevIt->second.linearVel + + tau*nextIt->second.linearVel) * SG_METER_TO_NM * 3600.0; + + if (prevIt->second.properties.size() + == nextIt->second.properties.size()) { + std::vector::const_iterator prevPropIt; + std::vector::const_iterator prevPropItEnd; + std::vector::const_iterator nextPropIt; + std::vector::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) { + PropertyMap::iterator pIt = mPropertyMap.find((*prevPropIt)->id); + //cout << " Setting property..." << (*prevPropIt)->id; + + if (pIt != mPropertyMap.end()) + { + //cout << "Found " << pIt->second->getPath() << ":"; + + int ival; + float val; + switch ((*prevPropIt)->type) { + case props::INT: + case props::BOOL: + case props::LONG: + ival = (int) (0.5+(1-tau)*((double) (*prevPropIt)->int_value) + + tau*((double) (*nextPropIt)->int_value)); + pIt->second->setIntValue(ival); + //cout << "Int: " << ival << "\n"; + break; + case props::FLOAT: + case props::DOUBLE: + val = (1-tau)*(*prevPropIt)->float_value + + tau*(*nextPropIt)->float_value; + //cout << "Flo: " << val << "\n"; + pIt->second->setFloatValue(val); + break; + case props::STRING: + case props::UNSPECIFIED: + //cout << "Str: " << (*nextPropIt)->string_value << "\n"; + pIt->second->setStringValue((*nextPropIt)->string_value); + break; + default: + // FIXME - currently defaults to float values + val = (1-tau)*(*prevPropIt)->float_value + + tau*(*nextPropIt)->float_value; + //cout << "Unk: " << val << "\n"; + pIt->second->setFloatValue(val); + break; + } + } + else + { + SG_LOG(SG_GENERAL, SG_DEBUG, "Unable to find property: " << (*prevPropIt)->id << "\n"); + } + + ++prevPropIt; + ++nextPropIt; + } + } + + // Now throw away too old data + if (prevIt != mMotionInfo.begin()) + { + --prevIt; + + MotionInfo::iterator delIt; + delIt = mMotionInfo.begin(); + + while (delIt != prevIt) + { + std::vector::const_iterator propIt; + std::vector::const_iterator propItEnd; + propIt = delIt->second.properties.begin(); + propItEnd = delIt->second.properties.end(); + + //cout << "Deleting data\n"; + + while (propIt != propItEnd) + { + delete *propIt; + propIt++; + } + + delIt++; + } + + mMotionInfo.erase(mMotionInfo.begin(), prevIt); + } + } + } 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::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; + } + + std::vector::const_iterator firstPropIt; + std::vector::const_iterator firstPropItEnd; + speed = norm(linearVel) * SG_METER_TO_NM * 3600.0; + firstPropIt = it->second.properties.begin(); + firstPropItEnd = it->second.properties.end(); + while (firstPropIt != firstPropItEnd) { + PropertyMap::iterator pIt = mPropertyMap.find((*firstPropIt)->id); + //cout << " Setting property..." << (*firstPropIt)->id; + + if (pIt != mPropertyMap.end()) + { + switch ((*firstPropIt)->type) { + case props::INT: + case props::BOOL: + case props::LONG: + pIt->second->setIntValue((*firstPropIt)->int_value); + //cout << "Int: " << (*firstPropIt)->int_value << "\n"; + break; + case props::FLOAT: + case props::DOUBLE: + pIt->second->setFloatValue((*firstPropIt)->float_value); + //cout << "Flo: " << (*firstPropIt)->float_value << "\n"; + break; + case props::STRING: + case props::UNSPECIFIED: + pIt->second->setStringValue((*firstPropIt)->string_value); + //cout << "Str: " << (*firstPropIt)->string_value << "\n"; + break; + default: + // FIXME - currently defaults to float values + pIt->second->setFloatValue((*firstPropIt)->float_value); + //cout << "Unk: " << (*firstPropIt)->float_value << "\n"; + break; + } + } + else + { + SG_LOG(SG_GENERAL, SG_DEBUG, "Unable to find property: " << (*firstPropIt)->id << "\n"); + } + + ++firstPropIt; + } + } + + // extract the position + pos = SGGeod::fromCart(ecPos); + altitude_ft = pos.getElevationFt(); + + // The quaternion rotating from the earth centered frame to the + // horizontal local frame + SGQuatf qEc2Hl = SGQuatf::fromLonLatRad((float)pos.getLongitudeRad(), + (float)pos.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: " + << ecPos << ", " << hlOr); + + //###########################// + // do calculations for radar // + //###########################// + double range_ft2 = UpdateRadar(manager); + + //************************************// + // Tanker code // + //************************************// + + + if ( isTanker) { + //cout << "IS tanker "; + if ( (range_ft2 < 250.0 * 250.0) && + (y_shift > 0.0) && + (elevation > 0.0) ){ + // refuel_node->setBoolValue(true); + //cout << "in contact" << endl; + contact = true; + } else { + // refuel_node->setBoolValue(false); + //cout << "not in contact" << endl; + contact = false; + } + } else { + //cout << "NOT tanker " << endl; + contact = false; + } + + Transform(); +} + +void +FGAIMultiplayer::addMotionInfo(const FGExternalMotionData& motionInfo, + long stamp) +{ + mLastTimestamp = stamp; + + if (!mMotionInfo.empty()) { + double diff = motionInfo.time - mMotionInfo.rbegin()->first; + + // packet is very old -- MP has probably reset (incl. his timebase) + if (diff < -10.0) + mMotionInfo.clear(); + + // drop packets arriving out of order + else if (diff < 0.0) + return; + } + mMotionInfo[motionInfo.time] = motionInfo; +} + +void +FGAIMultiplayer::setDoubleProperty(const std::string& prop, double val) +{ + SGPropertyNode* pNode = props->getChild(prop.c_str(), true); + pNode->setDoubleValue(val); +} From a9062080b0544f5aa8c6f359a8888fcdf94ab827 Mon Sep 17 00:00:00 2001 From: Anders Gidenstam Date: Thu, 2 Sep 2010 20:07:34 +0200 Subject: [PATCH 2/5] Restored line endings in src/MultiPlayer/multiplaymgr.cxx. Please check that the line endings are not messed up before mergeing. --- src/MultiPlayer/multiplaymgr.cxx | 2090 +++++++++++++++--------------- 1 file changed, 1045 insertions(+), 1045 deletions(-) diff --git a/src/MultiPlayer/multiplaymgr.cxx b/src/MultiPlayer/multiplaymgr.cxx index 8d4783315..f20777714 100644 --- a/src/MultiPlayer/multiplaymgr.cxx +++ b/src/MultiPlayer/multiplaymgr.cxx @@ -1,1045 +1,1045 @@ -////////////////////////////////////////////////////////////////////// -// -// multiplaymgr.hpp -// -// Written by Duncan McCreanor, started February 2003. -// duncan.mccreanor@airservicesaustralia.com -// -// 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 -// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// $Id$ -// -////////////////////////////////////////////////////////////////////// - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include // isNaN -#include - -#include -#include -#include -#include - -#include -#include
-#include "multiplaymgr.hxx" -#include "mpmessages.hxx" - -using namespace std; - -#define MAX_PACKET_SIZE 1200 -#define MAX_TEXT_SIZE 128 - -// These constants are provided so that the ident -// command can list file versions -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 -const FGMultiplayMgr::IdPropertyList -FGMultiplayMgr::sIdPropertyList[] = { - {100, "surface-positions/left-aileron-pos-norm", simgear::props::FLOAT}, - {101, "surface-positions/right-aileron-pos-norm", simgear::props::FLOAT}, - {102, "surface-positions/elevator-pos-norm", simgear::props::FLOAT}, - {103, "surface-positions/rudder-pos-norm", simgear::props::FLOAT}, - {104, "surface-positions/flap-pos-norm", simgear::props::FLOAT}, - {105, "surface-positions/speedbrake-pos-norm", simgear::props::FLOAT}, - {106, "gear/tailhook/position-norm", simgear::props::FLOAT}, - {107, "gear/launchbar/position-norm", simgear::props::FLOAT}, - {108, "gear/launchbar/state", simgear::props::STRING}, - {109, "gear/launchbar/holdback-position-norm", simgear::props::FLOAT}, - {110, "canopy/position-norm", simgear::props::FLOAT}, - {111, "surface-positions/wing-pos-norm", simgear::props::FLOAT}, - {112, "surface-positions/wing-fold-pos-norm", simgear::props::FLOAT}, - - {200, "gear/gear[0]/compression-norm", simgear::props::FLOAT}, - {201, "gear/gear[0]/position-norm", simgear::props::FLOAT}, - {210, "gear/gear[1]/compression-norm", simgear::props::FLOAT}, - {211, "gear/gear[1]/position-norm", simgear::props::FLOAT}, - {220, "gear/gear[2]/compression-norm", simgear::props::FLOAT}, - {221, "gear/gear[2]/position-norm", simgear::props::FLOAT}, - {230, "gear/gear[3]/compression-norm", simgear::props::FLOAT}, - {231, "gear/gear[3]/position-norm", simgear::props::FLOAT}, - {240, "gear/gear[4]/compression-norm", simgear::props::FLOAT}, - {241, "gear/gear[4]/position-norm", simgear::props::FLOAT}, - - {300, "engines/engine[0]/n1", simgear::props::FLOAT}, - {301, "engines/engine[0]/n2", simgear::props::FLOAT}, - {302, "engines/engine[0]/rpm", simgear::props::FLOAT}, - {310, "engines/engine[1]/n1", simgear::props::FLOAT}, - {311, "engines/engine[1]/n2", simgear::props::FLOAT}, - {312, "engines/engine[1]/rpm", simgear::props::FLOAT}, - {320, "engines/engine[2]/n1", simgear::props::FLOAT}, - {321, "engines/engine[2]/n2", simgear::props::FLOAT}, - {322, "engines/engine[2]/rpm", simgear::props::FLOAT}, - {330, "engines/engine[3]/n1", simgear::props::FLOAT}, - {331, "engines/engine[3]/n2", simgear::props::FLOAT}, - {332, "engines/engine[3]/rpm", simgear::props::FLOAT}, - {340, "engines/engine[4]/n1", simgear::props::FLOAT}, - {341, "engines/engine[4]/n2", simgear::props::FLOAT}, - {342, "engines/engine[4]/rpm", simgear::props::FLOAT}, - {350, "engines/engine[5]/n1", simgear::props::FLOAT}, - {351, "engines/engine[5]/n2", simgear::props::FLOAT}, - {352, "engines/engine[5]/rpm", simgear::props::FLOAT}, - {360, "engines/engine[6]/n1", simgear::props::FLOAT}, - {361, "engines/engine[6]/n2", simgear::props::FLOAT}, - {362, "engines/engine[6]/rpm", simgear::props::FLOAT}, - {370, "engines/engine[7]/n1", simgear::props::FLOAT}, - {371, "engines/engine[7]/n2", simgear::props::FLOAT}, - {372, "engines/engine[7]/rpm", simgear::props::FLOAT}, - {380, "engines/engine[8]/n1", simgear::props::FLOAT}, - {381, "engines/engine[8]/n2", simgear::props::FLOAT}, - {382, "engines/engine[8]/rpm", simgear::props::FLOAT}, - {390, "engines/engine[9]/n1", simgear::props::FLOAT}, - {391, "engines/engine[9]/n2", simgear::props::FLOAT}, - {392, "engines/engine[9]/rpm", simgear::props::FLOAT}, - - {800, "rotors/main/rpm", simgear::props::FLOAT}, - {801, "rotors/tail/rpm", simgear::props::FLOAT}, - {810, "rotors/main/blade[0]/position-deg", simgear::props::FLOAT}, - {811, "rotors/main/blade[1]/position-deg", simgear::props::FLOAT}, - {812, "rotors/main/blade[2]/position-deg", simgear::props::FLOAT}, - {813, "rotors/main/blade[3]/position-deg", simgear::props::FLOAT}, - {820, "rotors/main/blade[0]/flap-deg", simgear::props::FLOAT}, - {821, "rotors/main/blade[1]/flap-deg", simgear::props::FLOAT}, - {822, "rotors/main/blade[2]/flap-deg", simgear::props::FLOAT}, - {823, "rotors/main/blade[3]/flap-deg", simgear::props::FLOAT}, - {830, "rotors/tail/blade[0]/position-deg", simgear::props::FLOAT}, - {831, "rotors/tail/blade[1]/position-deg", simgear::props::FLOAT}, - - {900, "sim/hitches/aerotow/tow/length", simgear::props::FLOAT}, - {901, "sim/hitches/aerotow/tow/elastic-constant", simgear::props::FLOAT}, - {902, "sim/hitches/aerotow/tow/weight-per-m-kg-m", simgear::props::FLOAT}, - {903, "sim/hitches/aerotow/tow/dist", simgear::props::FLOAT}, - {904, "sim/hitches/aerotow/tow/connected-to-property-node", simgear::props::BOOL}, - {905, "sim/hitches/aerotow/tow/connected-to-ai-or-mp-callsign", simgear::props::STRING}, - {906, "sim/hitches/aerotow/tow/brake-force", simgear::props::FLOAT}, - {907, "sim/hitches/aerotow/tow/end-force-x", simgear::props::FLOAT}, - {908, "sim/hitches/aerotow/tow/end-force-y", simgear::props::FLOAT}, - {909, "sim/hitches/aerotow/tow/end-force-z", simgear::props::FLOAT}, - {930, "sim/hitches/aerotow/is-slave", simgear::props::BOOL}, - {931, "sim/hitches/aerotow/speed-in-tow-direction", simgear::props::FLOAT}, - {932, "sim/hitches/aerotow/open", simgear::props::BOOL}, - {933, "sim/hitches/aerotow/local-pos-x", simgear::props::FLOAT}, - {934, "sim/hitches/aerotow/local-pos-y", simgear::props::FLOAT}, - {935, "sim/hitches/aerotow/local-pos-z", simgear::props::FLOAT}, - - {1001, "controls/flight/slats", simgear::props::FLOAT}, - {1002, "controls/flight/speedbrake", simgear::props::FLOAT}, - {1003, "controls/flight/spoilers", simgear::props::FLOAT}, - {1004, "controls/gear/gear-down", simgear::props::FLOAT}, - {1005, "controls/lighting/nav-lights", simgear::props::FLOAT}, - {1006, "controls/armament/station[0]/jettison-all", simgear::props::BOOL}, - - {1100, "sim/model/variant", simgear::props::INT}, - {1101, "sim/model/livery/file", simgear::props::STRING}, - - {1200, "environment/wildfire/data", simgear::props::STRING}, - - {1300, "tanker", simgear::props::INT}, - - {10001, "sim/multiplay/transmission-freq-hz", simgear::props::STRING}, - {10002, "sim/multiplay/chat", simgear::props::STRING}, - - {10100, "sim/multiplay/generic/string[0]", simgear::props::STRING}, - {10101, "sim/multiplay/generic/string[1]", simgear::props::STRING}, - {10102, "sim/multiplay/generic/string[2]", simgear::props::STRING}, - {10103, "sim/multiplay/generic/string[3]", simgear::props::STRING}, - {10104, "sim/multiplay/generic/string[4]", simgear::props::STRING}, - {10105, "sim/multiplay/generic/string[5]", simgear::props::STRING}, - {10106, "sim/multiplay/generic/string[6]", simgear::props::STRING}, - {10107, "sim/multiplay/generic/string[7]", simgear::props::STRING}, - {10108, "sim/multiplay/generic/string[8]", simgear::props::STRING}, - {10109, "sim/multiplay/generic/string[9]", simgear::props::STRING}, - {10110, "sim/multiplay/generic/string[10]", simgear::props::STRING}, - {10111, "sim/multiplay/generic/string[11]", simgear::props::STRING}, - {10112, "sim/multiplay/generic/string[12]", simgear::props::STRING}, - {10113, "sim/multiplay/generic/string[13]", simgear::props::STRING}, - {10114, "sim/multiplay/generic/string[14]", simgear::props::STRING}, - {10115, "sim/multiplay/generic/string[15]", simgear::props::STRING}, - {10116, "sim/multiplay/generic/string[16]", simgear::props::STRING}, - {10117, "sim/multiplay/generic/string[17]", simgear::props::STRING}, - {10118, "sim/multiplay/generic/string[18]", simgear::props::STRING}, - {10119, "sim/multiplay/generic/string[19]", simgear::props::STRING}, - - {10200, "sim/multiplay/generic/float[0]", simgear::props::FLOAT}, - {10201, "sim/multiplay/generic/float[1]", simgear::props::FLOAT}, - {10202, "sim/multiplay/generic/float[2]", simgear::props::FLOAT}, - {10203, "sim/multiplay/generic/float[3]", simgear::props::FLOAT}, - {10204, "sim/multiplay/generic/float[4]", simgear::props::FLOAT}, - {10205, "sim/multiplay/generic/float[5]", simgear::props::FLOAT}, - {10206, "sim/multiplay/generic/float[6]", simgear::props::FLOAT}, - {10207, "sim/multiplay/generic/float[7]", simgear::props::FLOAT}, - {10208, "sim/multiplay/generic/float[8]", simgear::props::FLOAT}, - {10209, "sim/multiplay/generic/float[9]", simgear::props::FLOAT}, - {10210, "sim/multiplay/generic/float[10]", simgear::props::FLOAT}, - {10211, "sim/multiplay/generic/float[11]", simgear::props::FLOAT}, - {10212, "sim/multiplay/generic/float[12]", simgear::props::FLOAT}, - {10213, "sim/multiplay/generic/float[13]", simgear::props::FLOAT}, - {10214, "sim/multiplay/generic/float[14]", simgear::props::FLOAT}, - {10215, "sim/multiplay/generic/float[15]", simgear::props::FLOAT}, - {10216, "sim/multiplay/generic/float[16]", simgear::props::FLOAT}, - {10217, "sim/multiplay/generic/float[17]", simgear::props::FLOAT}, - {10218, "sim/multiplay/generic/float[18]", simgear::props::FLOAT}, - {10219, "sim/multiplay/generic/float[19]", simgear::props::FLOAT}, - - {10300, "sim/multiplay/generic/int[0]", simgear::props::INT}, - {10301, "sim/multiplay/generic/int[1]", simgear::props::INT}, - {10302, "sim/multiplay/generic/int[2]", simgear::props::INT}, - {10303, "sim/multiplay/generic/int[3]", simgear::props::INT}, - {10304, "sim/multiplay/generic/int[4]", simgear::props::INT}, - {10305, "sim/multiplay/generic/int[5]", simgear::props::INT}, - {10306, "sim/multiplay/generic/int[6]", simgear::props::INT}, - {10307, "sim/multiplay/generic/int[7]", simgear::props::INT}, - {10308, "sim/multiplay/generic/int[8]", simgear::props::INT}, - {10309, "sim/multiplay/generic/int[9]", simgear::props::INT}, - {10310, "sim/multiplay/generic/int[10]", simgear::props::INT}, - {10311, "sim/multiplay/generic/int[11]", simgear::props::INT}, - {10312, "sim/multiplay/generic/int[12]", simgear::props::INT}, - {10313, "sim/multiplay/generic/int[13]", simgear::props::INT}, - {10314, "sim/multiplay/generic/int[14]", simgear::props::INT}, - {10315, "sim/multiplay/generic/int[15]", simgear::props::INT}, - {10316, "sim/multiplay/generic/int[16]", simgear::props::INT}, - {10317, "sim/multiplay/generic/int[17]", simgear::props::INT}, - {10318, "sim/multiplay/generic/int[18]", simgear::props::INT}, - {10319, "sim/multiplay/generic/int[19]", simgear::props::INT} -}; - -const unsigned -FGMultiplayMgr::numProperties = (sizeof(FGMultiplayMgr::sIdPropertyList) - / sizeof(FGMultiplayMgr::sIdPropertyList[0])); - -// Look up a property ID using binary search. -namespace -{ - struct ComparePropertyId - { - bool operator()(const FGMultiplayMgr::IdPropertyList& lhs, - const FGMultiplayMgr::IdPropertyList& rhs) - { - return lhs.id < rhs.id; - } - bool operator()(const FGMultiplayMgr::IdPropertyList& lhs, - unsigned id) - { - return lhs.id < id; - } - bool operator()(unsigned id, - const FGMultiplayMgr::IdPropertyList& rhs) - { - return id < rhs.id; - } - }; - -} -const FGMultiplayMgr::IdPropertyList* FGMultiplayMgr::findProperty(unsigned id) -{ - std::pair result - = std::equal_range(sIdPropertyList, sIdPropertyList + numProperties, id, - ComparePropertyId()); - if (result.first == result.second) { - return 0; - } else { - return result.first; - } -} - -namespace -{ - bool verifyProperties(const xdr_data_t* data, const xdr_data_t* end) - { - using namespace simgear; - const xdr_data_t* xdr = data; - while (xdr < end) { - unsigned id = XDR_decode_uint32(*xdr); - const FGMultiplayMgr::IdPropertyList* plist - = FGMultiplayMgr::findProperty(id); - - if (plist) { - xdr++; - // How we decode the remainder of the property depends on the type - switch (plist->type) { - case props::INT: - case props::BOOL: - case props::LONG: - xdr++; - break; - case props::FLOAT: - case props::DOUBLE: - { - float val = XDR_decode_float(*xdr); - if (osg::isNaN(val)) - return false; - xdr++; - break; - } - case props::STRING: - case props::UNSPECIFIED: - { - // String is complicated. It consists of - // The length of the string - // The string itself - // Padding to the nearest 4-bytes. - // XXX Yes, each byte is padded out to a word! Too late - // to change... - uint32_t length = XDR_decode_uint32(*xdr); - xdr++; - // Old versions truncated the string but left the length - // unadjusted. - if (length > MAX_TEXT_SIZE) - length = MAX_TEXT_SIZE; - xdr += length; - // Now handle the padding - while ((length % 4) != 0) - { - xdr++; - length++; - //cout << "0"; - } - } - break; - default: - // cerr << "Unknown Prop type " << id << " " << type << "\n"; - xdr++; - break; - } - } - else { - // give up; this is a malformed property list. - return false; - } - } - return true; - } -} -////////////////////////////////////////////////////////////////////// -// -// MultiplayMgr constructor -// -////////////////////////////////////////////////////////////////////// -FGMultiplayMgr::FGMultiplayMgr() -{ - mSocket = 0; - mInitialised = false; - mHaveServer = false; -} // FGMultiplayMgr::FGMultiplayMgr() -////////////////////////////////////////////////////////////////////// - -////////////////////////////////////////////////////////////////////// -// -// MultiplayMgr destructor -// -////////////////////////////////////////////////////////////////////// -FGMultiplayMgr::~FGMultiplayMgr() -{ - Close(); -} // FGMultiplayMgr::~FGMultiplayMgr() -////////////////////////////////////////////////////////////////////// - -////////////////////////////////////////////////////////////////////// -// -// Initialise object -// -////////////////////////////////////////////////////////////////////// -bool -FGMultiplayMgr::init (void) -{ - ////////////////////////////////////////////////// - // 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"); - string rxAddress = fgGetString("/sim/multiplay/rxhost"); - short txPort = fgGetInt("/sim/multiplay/txport"); - string txAddress = fgGetString("/sim/multiplay/txhost"); - mCallsign = fgGetString("/sim/multiplay/callsign"); - if (txPort > 0 && !txAddress.empty()) { - mServer.set(txAddress.c_str(), txPort); - if (strncmp (mServer.getHost(), "0.0.0.0", 8) == 0) { - mHaveServer = false; - SG_LOG(SG_NETWORK, SG_DEBUG, - "FGMultiplayMgr - could not resolve '" - << txAddress << "', Multiplayermode disabled"); - } else { - mHaveServer = true; - } - if (rxPort <= 0) - rxPort = txPort; - } - if (rxPort <= 0) { - SG_LOG(SG_NETWORK, SG_DEBUG, - "FGMultiplayMgr - No receiver port, Multiplayermode disabled"); - return (false); - } - if (mCallsign.empty()) - mCallsign = "JohnDoe"; // FIXME: use getpwuid - SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-txaddress= "<setBlocking(false); - if (mSocket->bind(rxAddress.c_str(), rxPort) != 0) { - perror("bind"); - SG_LOG( SG_NETWORK, SG_DEBUG, - "FGMultiplayMgr::Open - Failed to bind receive socket" ); - return false; - } - mInitialised = true; - return true; -} // FGMultiplayMgr::init() -////////////////////////////////////////////////////////////////////// - -////////////////////////////////////////////////////////////////////// -// -// Closes and deletes the local player object. Closes -// and deletes the tx socket. Resets the object state to unitialised. -// -////////////////////////////////////////////////////////////////////// -void -FGMultiplayMgr::Close (void) -{ - mMultiPlayerMap.clear(); - - if (mSocket) { - mSocket->close(); - delete mSocket; - mSocket = 0; - } - mInitialised = false; -} // FGMultiplayMgr::Close(void) -////////////////////////////////////////////////////////////////////// - -////////////////////////////////////////////////////////////////////// -// -// Description: Sends the position data for the local position. -// -////////////////////////////////////////////////////////////////////// - -/** - * The buffer that holds a multi-player message, suitably aligned. - */ -union FGMultiplayMgr::MsgBuf -{ - MsgBuf() - { - memset(&Msg, 0, sizeof(Msg)); - } - - T_MsgHdr* msgHdr() - { - return reinterpret_cast(Msg); - } - - const T_MsgHdr* msgHdr() const - { - return reinterpret_cast(Msg); - } - - T_PositionMsg* posMsg() - { - return reinterpret_cast(Msg + sizeof(T_MsgHdr)); - } - - const T_PositionMsg* posMsg() const - { - return reinterpret_cast(Msg + sizeof(T_MsgHdr)); - } - - xdr_data_t* properties() - { - return reinterpret_cast(Msg + sizeof(T_MsgHdr) - + sizeof(T_PositionMsg)); - } - - const xdr_data_t* properties() const - { - return reinterpret_cast(Msg + sizeof(T_MsgHdr) - + sizeof(T_PositionMsg)); - } - /** - * The end of the properties buffer. - */ - xdr_data_t* propsEnd() - { - return reinterpret_cast(Msg + MAX_PACKET_SIZE); - }; - - const xdr_data_t* propsEnd() const - { - return reinterpret_cast(Msg + MAX_PACKET_SIZE); - }; - /** - * The end of properties actually in the buffer. This assumes that - * the message header is valid. - */ - xdr_data_t* propsRecvdEnd() - { - return reinterpret_cast(Msg + msgHdr()->MsgLen); - } - - const xdr_data_t* propsRecvdEnd() const - { - return reinterpret_cast(Msg + msgHdr()->MsgLen); - } - - xdr_data2_t double_val; - char Msg[MAX_PACKET_SIZE]; -}; - -void -FGMultiplayMgr::SendMyPosition(const FGExternalMotionData& motionInfo) -{ - if ((! mInitialised) || (! mHaveServer)) - return; - if (! mHaveServer) { - SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::SendMyPosition - no server"); - return; - } - - MsgBuf msgBuf; - T_PositionMsg* PosMsg = msgBuf.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)); - - xdr_data_t* ptr = msgBuf.properties(); - std::vector::const_iterator it; - it = motionInfo.properties.begin(); - //cout << "OUTPUT PROPERTIES\n"; - xdr_data_t* msgEnd = msgBuf.propsEnd(); - while (it != motionInfo.properties.end() && ptr + 2 < msgEnd) { - - // First element is the ID. Write it out when we know we have room for - // the whole property. - xdr_data_t id = XDR_encode_uint32((*it)->id); - // The actual data representation depends on the type - switch ((*it)->type) { - case simgear::props::INT: - case simgear::props::BOOL: - case simgear::props::LONG: - *ptr++ = id; - *ptr++ = XDR_encode_uint32((*it)->int_value); - //cout << "Prop:" << (*it)->id << " " << (*it)->type << " "<< (*it)->int_value << "\n"; - break; - case simgear::props::FLOAT: - case simgear::props::DOUBLE: - *ptr++ = id; - *ptr++ = XDR_encode_float((*it)->float_value); - //cout << "Prop:" << (*it)->id << " " << (*it)->type << " "<< (*it)->float_value << "\n"; - break; - case simgear::props::STRING: - case simgear::props::UNSPECIFIED: - { - // String is complicated. It consists of - // The length of the string - // The string itself - // Padding to the nearest 4-bytes. - const char* lcharptr = (*it)->string_value; - - if (lcharptr != 0) - { - // Add the length - ////cout << "String length: " << strlen(lcharptr) << "\n"; - uint32_t len = strlen(lcharptr); - if (len > MAX_TEXT_SIZE) - len = MAX_TEXT_SIZE; - // XXX This should not be using 4 bytes per character! - // If there's not enough room for this property, drop it - // on the floor. - if (ptr + 2 + ((len + 3) & ~3) > msgEnd) - goto escape; - //cout << "String length unint32: " << len << "\n"; - *ptr++ = id; - *ptr++ = XDR_encode_uint32(len); - if (len != 0) - { - // Now the text itself - // XXX This should not be using 4 bytes per character! - int lcount = 0; - while ((*lcharptr != '\0') && (lcount < MAX_TEXT_SIZE)) - { - *ptr++ = XDR_encode_int8(*lcharptr); - lcharptr++; - lcount++; - } - - //cout << "Prop:" << (*it)->id << " " << (*it)->type << " " << len << " " << (*it)->string_value; - - // Now pad if required - while ((lcount % 4) != 0) - { - *ptr++ = XDR_encode_int8(0); - lcount++; - //cout << "0"; - } - - //cout << "\n"; - } - } - else - { - // Nothing to encode - *ptr++ = id; - *ptr++ = XDR_encode_uint32(0); - //cout << "Prop:" << (*it)->id << " " << (*it)->type << " 0\n"; - } - } - break; - - default: - //cout << " Unknown Type: " << (*it)->type << "\n"; - *ptr++ = id; - *ptr++ = XDR_encode_float((*it)->float_value);; - //cout << "Prop:" << (*it)->id << " " << (*it)->type << " "<< (*it)->float_value << "\n"; - break; - } - - ++it; - } -escape: - unsigned msgLen = reinterpret_cast(ptr) - msgBuf.Msg; - FillMsgHdr(msgBuf.msgHdr(), POS_DATA_ID, msgLen); - mSocket->sendto(msgBuf.Msg, msgLen, 0, &mServer); - SG_LOG(SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::SendMyPosition"); -} // FGMultiplayMgr::SendMyPosition() - -////////////////////////////////////////////////////////////////////// - -////////////////////////////////////////////////////////////////////// -// -// Name: SendTextMessage -// Description: Sends a message to the player. The message must -// contain a valid and correctly filled out header and optional -// message body. -// -////////////////////////////////////////////////////////////////////// -void -FGMultiplayMgr::SendTextMessage(const string &MsgText) -{ - 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 () -////////////////////////////////////////////////////////////////////// - -////////////////////////////////////////////////////////////////////// -// -// Name: ProcessData -// Description: Processes data waiting at the receive socket. The -// processing ends when there is no more data at the socket. -// -////////////////////////////////////////////////////////////////////// -void -FGMultiplayMgr::Update(void) -{ - if (!mInitialised) - return; - - /// Just for expiry - long stamp = SGTimeStamp::now().getSeconds(); - - ////////////////////////////////////////////////// - // Read the receive socket and process any data - ////////////////////////////////////////////////// - ssize_t bytes; - do { - MsgBuf msgBuf; - ////////////////////////////////////////////////// - // 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(msgBuf.Msg, sizeof(msgBuf.Msg), 0, - &SenderAddress); - ////////////////////////////////////////////////// - // no Data received - ////////////////////////////////////////////////// - if (bytes <= 0) { - if (errno != EAGAIN && errno != 0) // MSVC output "NoError" otherwise - perror("FGMultiplayMgr::MP_ProcessData"); - break; - } - if (bytes <= static_cast(sizeof(T_MsgHdr))) { - SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - " - << "received message with insufficient data" ); - break; - } - ////////////////////////////////////////////////// - // Read header - ////////////////////////////////////////////////// - T_MsgHdr* MsgHdr = msgBuf.msgHdr(); - 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); - MsgHdr->Callsign[MAX_CALLSIGN_LEN -1] = '\0'; - if (MsgHdr->Magic != MSG_MAGIC) { - SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - " - << "message has invalid magic number!" ); - break; - } - if (MsgHdr->Version != PROTO_VER) { - SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - " - << "message has invalid protocoll number!" ); - break; - } - if (MsgHdr->MsgLen != bytes) { - SG_LOG(SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - " - << "message from " << MsgHdr->Callsign << " has invalid length!"); - break; - } - ////////////////////////////////////////////////// - // Process messages - ////////////////////////////////////////////////// - switch (MsgHdr->MsgId) { - case CHAT_MSG_ID: - ProcessChatMsg(msgBuf, SenderAddress); - break; - case POS_DATA_ID: - ProcessPosMsg(msgBuf, SenderAddress, 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_DEBUG, "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) -////////////////////////////////////////////////////////////////////// - -////////////////////////////////////////////////////////////////////// -// -// handle a position message -// -////////////////////////////////////////////////////////////////////// -void -FGMultiplayMgr::ProcessPosMsg(const FGMultiplayMgr::MsgBuf& Msg, - const netAddress& SenderAddress, long stamp) -{ - const T_MsgHdr* MsgHdr = Msg.msgHdr(); - if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + sizeof(T_PositionMsg)) { - SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - " - << "Position message received with insufficient data" ); - return; - } - const T_PositionMsg* PosMsg = Msg.posMsg(); - 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]); - - - //cout << "INPUT MESSAGE\n"; - - // There was a bug in 1.9.0 and before: T_PositionMsg was 196 bytes - // on 32 bit architectures and 200 bytes on 64 bit, and this - // structure is put directly on the wire. By looking at the padding, - // we can sort through the mess, mostly: - // If padding is 0 (which is not a valid property type), then the - // message was produced by a new client or an old 64 bit client that - // happened to have 0 on the stack; - // Else if the property list starting with the padding word is - // well-formed, then the client is probably an old 32 bit client and - // we'll go with that; - // Else it is an old 64-bit client and properties start after the - // padding. - // There is a chance that we could be fooled by garbage in the - // padding looking like a valid property, so verifyProperties() is - // strict about the validity of the property values. - const xdr_data_t* xdr = Msg.properties(); - if (PosMsg->pad != 0) { - if (verifyProperties(&PosMsg->pad, Msg.propsRecvdEnd())) - xdr = &PosMsg->pad; - else if (!verifyProperties(xdr, Msg.propsRecvdEnd())) - goto noprops; - } - while (xdr < Msg.propsRecvdEnd()) { - FGPropertyData* pData = new FGPropertyData; - // simgear::props::Type type = simgear::props::UNSPECIFIED; - - // First element is always the ID - pData->id = XDR_decode_uint32(*xdr); - //cout << pData->id << " "; - xdr++; - - // Check the ID actually exists and get the type - const IdPropertyList* plist = findProperty(pData->id); - - if (plist) - { - pData->type = plist->type; - // How we decode the remainder of the property depends on the type - switch (pData->type) { - case simgear::props::INT: - case simgear::props::BOOL: - case simgear::props::LONG: - pData->int_value = XDR_decode_uint32(*xdr); - xdr++; - //cout << pData->int_value << "\n"; - break; - case simgear::props::FLOAT: - case simgear::props::DOUBLE: - pData->float_value = XDR_decode_float(*xdr); - xdr++; - //cout << pData->float_value << "\n"; - break; - case simgear::props::STRING: - case simgear::props::UNSPECIFIED: - { - // String is complicated. It consists of - // The length of the string - // The string itself - // Padding to the nearest 4-bytes. - uint32_t length = XDR_decode_uint32(*xdr); - xdr++; - //cout << length << " "; - // Old versions truncated the string but left the length unadjusted. - if (length > MAX_TEXT_SIZE) - length = MAX_TEXT_SIZE; - pData->string_value = new char[length + 1]; - //cout << " String: "; - for (unsigned i = 0; i < length; i++) - { - pData->string_value[i] = (char) XDR_decode_int8(*xdr); - xdr++; - //cout << pData->string_value[i]; - } - - pData->string_value[length] = '\0'; - - // Now handle the padding - while ((length % 4) != 0) - { - xdr++; - length++; - //cout << "0"; - } - //cout << "\n"; - } - break; - - default: - pData->float_value = XDR_decode_float(*xdr); - SG_LOG(SG_NETWORK, SG_DEBUG, "Unknown Prop type " << pData->id << " " << pData->type); - xdr++; - break; - } - - motionInfo.properties.push_back(pData); - } - else - { - // We failed to find the property. We'll try the next packet immediately. - SG_LOG(SG_NETWORK, SG_INFO, "FGMultiplayMgr::ProcessPosMsg - " - "message from " << MsgHdr->Callsign << " has unknown property id " - << pData->id); - } - } - noprops: - FGAIMultiplayer* mp = getMultiplayer(MsgHdr->Callsign); - if (!mp) - mp = addMultiplayer(MsgHdr->Callsign, PosMsg->Model); - mp->addMotionInfo(motionInfo, stamp); -} // FGMultiplayMgr::ProcessPosMsg() -////////////////////////////////////////////////////////////////////// - -////////////////////////////////////////////////////////////////////// -// -// handle a chat message -// FIXME: display chat message withi flightgear -// -////////////////////////////////////////////////////////////////////// -void -FGMultiplayMgr::ProcessChatMsg(const MsgBuf& Msg, - const netAddress& SenderAddress) -{ - const T_MsgHdr* MsgHdr = Msg.msgHdr(); - if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + 1) { - SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - " - << "Chat message received with insufficient data" ); - return; - } - - char *chatStr = new char[MsgHdr->MsgLen - sizeof(T_MsgHdr)]; - const T_ChatMsg* ChatMsg - = reinterpret_cast(Msg.Msg + sizeof(T_MsgHdr)); - strncpy(chatStr, ChatMsg->Text, - MsgHdr->MsgLen - sizeof(T_MsgHdr)); - chatStr[MsgHdr->MsgLen - sizeof(T_MsgHdr) - 1] = '\0'; - - SG_LOG (SG_NETWORK, SG_WARN, "Chat [" << MsgHdr->Callsign << "]" - << " " << chatStr); - - delete [] chatStr; -} // FGMultiplayMgr::ProcessChatMsg () -////////////////////////////////////////////////////////////////////// - -void -FGMultiplayMgr::FillMsgHdr(T_MsgHdr *MsgHdr, int MsgId, unsigned _len) -{ - 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'; -} - -FGAIMultiplayer* -FGMultiplayMgr::addMultiplayer(const std::string& callsign, - const std::string& modelName) -{ - if (0 < mMultiPlayerMap.count(callsign)) - return mMultiPlayerMap[callsign].get(); - - 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 ... - for (unsigned i = 0; i < numProperties; ++i) - mp->addPropertyId(sIdPropertyList[i].id, sIdPropertyList[i].name); - } - - return mp; -} - -FGAIMultiplayer* -FGMultiplayMgr::getMultiplayer(const std::string& callsign) -{ - if (0 < mMultiPlayerMap.count(callsign)) - return mMultiPlayerMap[callsign].get(); - else - return 0; -} +////////////////////////////////////////////////////////////////////// +// +// multiplaymgr.hpp +// +// Written by Duncan McCreanor, started February 2003. +// duncan.mccreanor@airservicesaustralia.com +// +// 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 +// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ +// +////////////////////////////////////////////////////////////////////// + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include // isNaN +#include + +#include +#include +#include +#include + +#include +#include
+#include "multiplaymgr.hxx" +#include "mpmessages.hxx" + +using namespace std; + +#define MAX_PACKET_SIZE 1200 +#define MAX_TEXT_SIZE 128 + +// These constants are provided so that the ident +// command can list file versions +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 +const FGMultiplayMgr::IdPropertyList +FGMultiplayMgr::sIdPropertyList[] = { + {100, "surface-positions/left-aileron-pos-norm", simgear::props::FLOAT}, + {101, "surface-positions/right-aileron-pos-norm", simgear::props::FLOAT}, + {102, "surface-positions/elevator-pos-norm", simgear::props::FLOAT}, + {103, "surface-positions/rudder-pos-norm", simgear::props::FLOAT}, + {104, "surface-positions/flap-pos-norm", simgear::props::FLOAT}, + {105, "surface-positions/speedbrake-pos-norm", simgear::props::FLOAT}, + {106, "gear/tailhook/position-norm", simgear::props::FLOAT}, + {107, "gear/launchbar/position-norm", simgear::props::FLOAT}, + {108, "gear/launchbar/state", simgear::props::STRING}, + {109, "gear/launchbar/holdback-position-norm", simgear::props::FLOAT}, + {110, "canopy/position-norm", simgear::props::FLOAT}, + {111, "surface-positions/wing-pos-norm", simgear::props::FLOAT}, + {112, "surface-positions/wing-fold-pos-norm", simgear::props::FLOAT}, + + {200, "gear/gear[0]/compression-norm", simgear::props::FLOAT}, + {201, "gear/gear[0]/position-norm", simgear::props::FLOAT}, + {210, "gear/gear[1]/compression-norm", simgear::props::FLOAT}, + {211, "gear/gear[1]/position-norm", simgear::props::FLOAT}, + {220, "gear/gear[2]/compression-norm", simgear::props::FLOAT}, + {221, "gear/gear[2]/position-norm", simgear::props::FLOAT}, + {230, "gear/gear[3]/compression-norm", simgear::props::FLOAT}, + {231, "gear/gear[3]/position-norm", simgear::props::FLOAT}, + {240, "gear/gear[4]/compression-norm", simgear::props::FLOAT}, + {241, "gear/gear[4]/position-norm", simgear::props::FLOAT}, + + {300, "engines/engine[0]/n1", simgear::props::FLOAT}, + {301, "engines/engine[0]/n2", simgear::props::FLOAT}, + {302, "engines/engine[0]/rpm", simgear::props::FLOAT}, + {310, "engines/engine[1]/n1", simgear::props::FLOAT}, + {311, "engines/engine[1]/n2", simgear::props::FLOAT}, + {312, "engines/engine[1]/rpm", simgear::props::FLOAT}, + {320, "engines/engine[2]/n1", simgear::props::FLOAT}, + {321, "engines/engine[2]/n2", simgear::props::FLOAT}, + {322, "engines/engine[2]/rpm", simgear::props::FLOAT}, + {330, "engines/engine[3]/n1", simgear::props::FLOAT}, + {331, "engines/engine[3]/n2", simgear::props::FLOAT}, + {332, "engines/engine[3]/rpm", simgear::props::FLOAT}, + {340, "engines/engine[4]/n1", simgear::props::FLOAT}, + {341, "engines/engine[4]/n2", simgear::props::FLOAT}, + {342, "engines/engine[4]/rpm", simgear::props::FLOAT}, + {350, "engines/engine[5]/n1", simgear::props::FLOAT}, + {351, "engines/engine[5]/n2", simgear::props::FLOAT}, + {352, "engines/engine[5]/rpm", simgear::props::FLOAT}, + {360, "engines/engine[6]/n1", simgear::props::FLOAT}, + {361, "engines/engine[6]/n2", simgear::props::FLOAT}, + {362, "engines/engine[6]/rpm", simgear::props::FLOAT}, + {370, "engines/engine[7]/n1", simgear::props::FLOAT}, + {371, "engines/engine[7]/n2", simgear::props::FLOAT}, + {372, "engines/engine[7]/rpm", simgear::props::FLOAT}, + {380, "engines/engine[8]/n1", simgear::props::FLOAT}, + {381, "engines/engine[8]/n2", simgear::props::FLOAT}, + {382, "engines/engine[8]/rpm", simgear::props::FLOAT}, + {390, "engines/engine[9]/n1", simgear::props::FLOAT}, + {391, "engines/engine[9]/n2", simgear::props::FLOAT}, + {392, "engines/engine[9]/rpm", simgear::props::FLOAT}, + + {800, "rotors/main/rpm", simgear::props::FLOAT}, + {801, "rotors/tail/rpm", simgear::props::FLOAT}, + {810, "rotors/main/blade[0]/position-deg", simgear::props::FLOAT}, + {811, "rotors/main/blade[1]/position-deg", simgear::props::FLOAT}, + {812, "rotors/main/blade[2]/position-deg", simgear::props::FLOAT}, + {813, "rotors/main/blade[3]/position-deg", simgear::props::FLOAT}, + {820, "rotors/main/blade[0]/flap-deg", simgear::props::FLOAT}, + {821, "rotors/main/blade[1]/flap-deg", simgear::props::FLOAT}, + {822, "rotors/main/blade[2]/flap-deg", simgear::props::FLOAT}, + {823, "rotors/main/blade[3]/flap-deg", simgear::props::FLOAT}, + {830, "rotors/tail/blade[0]/position-deg", simgear::props::FLOAT}, + {831, "rotors/tail/blade[1]/position-deg", simgear::props::FLOAT}, + + {900, "sim/hitches/aerotow/tow/length", simgear::props::FLOAT}, + {901, "sim/hitches/aerotow/tow/elastic-constant", simgear::props::FLOAT}, + {902, "sim/hitches/aerotow/tow/weight-per-m-kg-m", simgear::props::FLOAT}, + {903, "sim/hitches/aerotow/tow/dist", simgear::props::FLOAT}, + {904, "sim/hitches/aerotow/tow/connected-to-property-node", simgear::props::BOOL}, + {905, "sim/hitches/aerotow/tow/connected-to-ai-or-mp-callsign", simgear::props::STRING}, + {906, "sim/hitches/aerotow/tow/brake-force", simgear::props::FLOAT}, + {907, "sim/hitches/aerotow/tow/end-force-x", simgear::props::FLOAT}, + {908, "sim/hitches/aerotow/tow/end-force-y", simgear::props::FLOAT}, + {909, "sim/hitches/aerotow/tow/end-force-z", simgear::props::FLOAT}, + {930, "sim/hitches/aerotow/is-slave", simgear::props::BOOL}, + {931, "sim/hitches/aerotow/speed-in-tow-direction", simgear::props::FLOAT}, + {932, "sim/hitches/aerotow/open", simgear::props::BOOL}, + {933, "sim/hitches/aerotow/local-pos-x", simgear::props::FLOAT}, + {934, "sim/hitches/aerotow/local-pos-y", simgear::props::FLOAT}, + {935, "sim/hitches/aerotow/local-pos-z", simgear::props::FLOAT}, + + {1001, "controls/flight/slats", simgear::props::FLOAT}, + {1002, "controls/flight/speedbrake", simgear::props::FLOAT}, + {1003, "controls/flight/spoilers", simgear::props::FLOAT}, + {1004, "controls/gear/gear-down", simgear::props::FLOAT}, + {1005, "controls/lighting/nav-lights", simgear::props::FLOAT}, + {1006, "controls/armament/station[0]/jettison-all", simgear::props::BOOL}, + + {1100, "sim/model/variant", simgear::props::INT}, + {1101, "sim/model/livery/file", simgear::props::STRING}, + + {1200, "environment/wildfire/data", simgear::props::STRING}, + + {1300, "tanker", simgear::props::INT}, + + {10001, "sim/multiplay/transmission-freq-hz", simgear::props::STRING}, + {10002, "sim/multiplay/chat", simgear::props::STRING}, + + {10100, "sim/multiplay/generic/string[0]", simgear::props::STRING}, + {10101, "sim/multiplay/generic/string[1]", simgear::props::STRING}, + {10102, "sim/multiplay/generic/string[2]", simgear::props::STRING}, + {10103, "sim/multiplay/generic/string[3]", simgear::props::STRING}, + {10104, "sim/multiplay/generic/string[4]", simgear::props::STRING}, + {10105, "sim/multiplay/generic/string[5]", simgear::props::STRING}, + {10106, "sim/multiplay/generic/string[6]", simgear::props::STRING}, + {10107, "sim/multiplay/generic/string[7]", simgear::props::STRING}, + {10108, "sim/multiplay/generic/string[8]", simgear::props::STRING}, + {10109, "sim/multiplay/generic/string[9]", simgear::props::STRING}, + {10110, "sim/multiplay/generic/string[10]", simgear::props::STRING}, + {10111, "sim/multiplay/generic/string[11]", simgear::props::STRING}, + {10112, "sim/multiplay/generic/string[12]", simgear::props::STRING}, + {10113, "sim/multiplay/generic/string[13]", simgear::props::STRING}, + {10114, "sim/multiplay/generic/string[14]", simgear::props::STRING}, + {10115, "sim/multiplay/generic/string[15]", simgear::props::STRING}, + {10116, "sim/multiplay/generic/string[16]", simgear::props::STRING}, + {10117, "sim/multiplay/generic/string[17]", simgear::props::STRING}, + {10118, "sim/multiplay/generic/string[18]", simgear::props::STRING}, + {10119, "sim/multiplay/generic/string[19]", simgear::props::STRING}, + + {10200, "sim/multiplay/generic/float[0]", simgear::props::FLOAT}, + {10201, "sim/multiplay/generic/float[1]", simgear::props::FLOAT}, + {10202, "sim/multiplay/generic/float[2]", simgear::props::FLOAT}, + {10203, "sim/multiplay/generic/float[3]", simgear::props::FLOAT}, + {10204, "sim/multiplay/generic/float[4]", simgear::props::FLOAT}, + {10205, "sim/multiplay/generic/float[5]", simgear::props::FLOAT}, + {10206, "sim/multiplay/generic/float[6]", simgear::props::FLOAT}, + {10207, "sim/multiplay/generic/float[7]", simgear::props::FLOAT}, + {10208, "sim/multiplay/generic/float[8]", simgear::props::FLOAT}, + {10209, "sim/multiplay/generic/float[9]", simgear::props::FLOAT}, + {10210, "sim/multiplay/generic/float[10]", simgear::props::FLOAT}, + {10211, "sim/multiplay/generic/float[11]", simgear::props::FLOAT}, + {10212, "sim/multiplay/generic/float[12]", simgear::props::FLOAT}, + {10213, "sim/multiplay/generic/float[13]", simgear::props::FLOAT}, + {10214, "sim/multiplay/generic/float[14]", simgear::props::FLOAT}, + {10215, "sim/multiplay/generic/float[15]", simgear::props::FLOAT}, + {10216, "sim/multiplay/generic/float[16]", simgear::props::FLOAT}, + {10217, "sim/multiplay/generic/float[17]", simgear::props::FLOAT}, + {10218, "sim/multiplay/generic/float[18]", simgear::props::FLOAT}, + {10219, "sim/multiplay/generic/float[19]", simgear::props::FLOAT}, + + {10300, "sim/multiplay/generic/int[0]", simgear::props::INT}, + {10301, "sim/multiplay/generic/int[1]", simgear::props::INT}, + {10302, "sim/multiplay/generic/int[2]", simgear::props::INT}, + {10303, "sim/multiplay/generic/int[3]", simgear::props::INT}, + {10304, "sim/multiplay/generic/int[4]", simgear::props::INT}, + {10305, "sim/multiplay/generic/int[5]", simgear::props::INT}, + {10306, "sim/multiplay/generic/int[6]", simgear::props::INT}, + {10307, "sim/multiplay/generic/int[7]", simgear::props::INT}, + {10308, "sim/multiplay/generic/int[8]", simgear::props::INT}, + {10309, "sim/multiplay/generic/int[9]", simgear::props::INT}, + {10310, "sim/multiplay/generic/int[10]", simgear::props::INT}, + {10311, "sim/multiplay/generic/int[11]", simgear::props::INT}, + {10312, "sim/multiplay/generic/int[12]", simgear::props::INT}, + {10313, "sim/multiplay/generic/int[13]", simgear::props::INT}, + {10314, "sim/multiplay/generic/int[14]", simgear::props::INT}, + {10315, "sim/multiplay/generic/int[15]", simgear::props::INT}, + {10316, "sim/multiplay/generic/int[16]", simgear::props::INT}, + {10317, "sim/multiplay/generic/int[17]", simgear::props::INT}, + {10318, "sim/multiplay/generic/int[18]", simgear::props::INT}, + {10319, "sim/multiplay/generic/int[19]", simgear::props::INT} +}; + +const unsigned +FGMultiplayMgr::numProperties = (sizeof(FGMultiplayMgr::sIdPropertyList) + / sizeof(FGMultiplayMgr::sIdPropertyList[0])); + +// Look up a property ID using binary search. +namespace +{ + struct ComparePropertyId + { + bool operator()(const FGMultiplayMgr::IdPropertyList& lhs, + const FGMultiplayMgr::IdPropertyList& rhs) + { + return lhs.id < rhs.id; + } + bool operator()(const FGMultiplayMgr::IdPropertyList& lhs, + unsigned id) + { + return lhs.id < id; + } + bool operator()(unsigned id, + const FGMultiplayMgr::IdPropertyList& rhs) + { + return id < rhs.id; + } + }; + +} +const FGMultiplayMgr::IdPropertyList* FGMultiplayMgr::findProperty(unsigned id) +{ + std::pair result + = std::equal_range(sIdPropertyList, sIdPropertyList + numProperties, id, + ComparePropertyId()); + if (result.first == result.second) { + return 0; + } else { + return result.first; + } +} + +namespace +{ + bool verifyProperties(const xdr_data_t* data, const xdr_data_t* end) + { + using namespace simgear; + const xdr_data_t* xdr = data; + while (xdr < end) { + unsigned id = XDR_decode_uint32(*xdr); + const FGMultiplayMgr::IdPropertyList* plist + = FGMultiplayMgr::findProperty(id); + + if (plist) { + xdr++; + // How we decode the remainder of the property depends on the type + switch (plist->type) { + case props::INT: + case props::BOOL: + case props::LONG: + xdr++; + break; + case props::FLOAT: + case props::DOUBLE: + { + float val = XDR_decode_float(*xdr); + if (osg::isNaN(val)) + return false; + xdr++; + break; + } + case props::STRING: + case props::UNSPECIFIED: + { + // String is complicated. It consists of + // The length of the string + // The string itself + // Padding to the nearest 4-bytes. + // XXX Yes, each byte is padded out to a word! Too late + // to change... + uint32_t length = XDR_decode_uint32(*xdr); + xdr++; + // Old versions truncated the string but left the length + // unadjusted. + if (length > MAX_TEXT_SIZE) + length = MAX_TEXT_SIZE; + xdr += length; + // Now handle the padding + while ((length % 4) != 0) + { + xdr++; + length++; + //cout << "0"; + } + } + break; + default: + // cerr << "Unknown Prop type " << id << " " << type << "\n"; + xdr++; + break; + } + } + else { + // give up; this is a malformed property list. + return false; + } + } + return true; + } +} +////////////////////////////////////////////////////////////////////// +// +// MultiplayMgr constructor +// +////////////////////////////////////////////////////////////////////// +FGMultiplayMgr::FGMultiplayMgr() +{ + mSocket = 0; + mInitialised = false; + mHaveServer = false; +} // FGMultiplayMgr::FGMultiplayMgr() +////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////// +// +// MultiplayMgr destructor +// +////////////////////////////////////////////////////////////////////// +FGMultiplayMgr::~FGMultiplayMgr() +{ + Close(); +} // FGMultiplayMgr::~FGMultiplayMgr() +////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////// +// +// Initialise object +// +////////////////////////////////////////////////////////////////////// +bool +FGMultiplayMgr::init (void) +{ + ////////////////////////////////////////////////// + // 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"); + string rxAddress = fgGetString("/sim/multiplay/rxhost"); + short txPort = fgGetInt("/sim/multiplay/txport"); + string txAddress = fgGetString("/sim/multiplay/txhost"); + mCallsign = fgGetString("/sim/multiplay/callsign"); + if (txPort > 0 && !txAddress.empty()) { + mServer.set(txAddress.c_str(), txPort); + if (strncmp (mServer.getHost(), "0.0.0.0", 8) == 0) { + mHaveServer = false; + SG_LOG(SG_NETWORK, SG_DEBUG, + "FGMultiplayMgr - could not resolve '" + << txAddress << "', Multiplayermode disabled"); + } else { + mHaveServer = true; + } + if (rxPort <= 0) + rxPort = txPort; + } + if (rxPort <= 0) { + SG_LOG(SG_NETWORK, SG_DEBUG, + "FGMultiplayMgr - No receiver port, Multiplayermode disabled"); + return (false); + } + if (mCallsign.empty()) + mCallsign = "JohnDoe"; // FIXME: use getpwuid + SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-txaddress= "<setBlocking(false); + if (mSocket->bind(rxAddress.c_str(), rxPort) != 0) { + perror("bind"); + SG_LOG( SG_NETWORK, SG_DEBUG, + "FGMultiplayMgr::Open - Failed to bind receive socket" ); + return false; + } + mInitialised = true; + return true; +} // FGMultiplayMgr::init() +////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////// +// +// Closes and deletes the local player object. Closes +// and deletes the tx socket. Resets the object state to unitialised. +// +////////////////////////////////////////////////////////////////////// +void +FGMultiplayMgr::Close (void) +{ + mMultiPlayerMap.clear(); + + if (mSocket) { + mSocket->close(); + delete mSocket; + mSocket = 0; + } + mInitialised = false; +} // FGMultiplayMgr::Close(void) +////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////// +// +// Description: Sends the position data for the local position. +// +////////////////////////////////////////////////////////////////////// + +/** + * The buffer that holds a multi-player message, suitably aligned. + */ +union FGMultiplayMgr::MsgBuf +{ + MsgBuf() + { + memset(&Msg, 0, sizeof(Msg)); + } + + T_MsgHdr* msgHdr() + { + return reinterpret_cast(Msg); + } + + const T_MsgHdr* msgHdr() const + { + return reinterpret_cast(Msg); + } + + T_PositionMsg* posMsg() + { + return reinterpret_cast(Msg + sizeof(T_MsgHdr)); + } + + const T_PositionMsg* posMsg() const + { + return reinterpret_cast(Msg + sizeof(T_MsgHdr)); + } + + xdr_data_t* properties() + { + return reinterpret_cast(Msg + sizeof(T_MsgHdr) + + sizeof(T_PositionMsg)); + } + + const xdr_data_t* properties() const + { + return reinterpret_cast(Msg + sizeof(T_MsgHdr) + + sizeof(T_PositionMsg)); + } + /** + * The end of the properties buffer. + */ + xdr_data_t* propsEnd() + { + return reinterpret_cast(Msg + MAX_PACKET_SIZE); + }; + + const xdr_data_t* propsEnd() const + { + return reinterpret_cast(Msg + MAX_PACKET_SIZE); + }; + /** + * The end of properties actually in the buffer. This assumes that + * the message header is valid. + */ + xdr_data_t* propsRecvdEnd() + { + return reinterpret_cast(Msg + msgHdr()->MsgLen); + } + + const xdr_data_t* propsRecvdEnd() const + { + return reinterpret_cast(Msg + msgHdr()->MsgLen); + } + + xdr_data2_t double_val; + char Msg[MAX_PACKET_SIZE]; +}; + +void +FGMultiplayMgr::SendMyPosition(const FGExternalMotionData& motionInfo) +{ + if ((! mInitialised) || (! mHaveServer)) + return; + if (! mHaveServer) { + SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::SendMyPosition - no server"); + return; + } + + MsgBuf msgBuf; + T_PositionMsg* PosMsg = msgBuf.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)); + + xdr_data_t* ptr = msgBuf.properties(); + std::vector::const_iterator it; + it = motionInfo.properties.begin(); + //cout << "OUTPUT PROPERTIES\n"; + xdr_data_t* msgEnd = msgBuf.propsEnd(); + while (it != motionInfo.properties.end() && ptr + 2 < msgEnd) { + + // First element is the ID. Write it out when we know we have room for + // the whole property. + xdr_data_t id = XDR_encode_uint32((*it)->id); + // The actual data representation depends on the type + switch ((*it)->type) { + case simgear::props::INT: + case simgear::props::BOOL: + case simgear::props::LONG: + *ptr++ = id; + *ptr++ = XDR_encode_uint32((*it)->int_value); + //cout << "Prop:" << (*it)->id << " " << (*it)->type << " "<< (*it)->int_value << "\n"; + break; + case simgear::props::FLOAT: + case simgear::props::DOUBLE: + *ptr++ = id; + *ptr++ = XDR_encode_float((*it)->float_value); + //cout << "Prop:" << (*it)->id << " " << (*it)->type << " "<< (*it)->float_value << "\n"; + break; + case simgear::props::STRING: + case simgear::props::UNSPECIFIED: + { + // String is complicated. It consists of + // The length of the string + // The string itself + // Padding to the nearest 4-bytes. + const char* lcharptr = (*it)->string_value; + + if (lcharptr != 0) + { + // Add the length + ////cout << "String length: " << strlen(lcharptr) << "\n"; + uint32_t len = strlen(lcharptr); + if (len > MAX_TEXT_SIZE) + len = MAX_TEXT_SIZE; + // XXX This should not be using 4 bytes per character! + // If there's not enough room for this property, drop it + // on the floor. + if (ptr + 2 + ((len + 3) & ~3) > msgEnd) + goto escape; + //cout << "String length unint32: " << len << "\n"; + *ptr++ = id; + *ptr++ = XDR_encode_uint32(len); + if (len != 0) + { + // Now the text itself + // XXX This should not be using 4 bytes per character! + int lcount = 0; + while ((*lcharptr != '\0') && (lcount < MAX_TEXT_SIZE)) + { + *ptr++ = XDR_encode_int8(*lcharptr); + lcharptr++; + lcount++; + } + + //cout << "Prop:" << (*it)->id << " " << (*it)->type << " " << len << " " << (*it)->string_value; + + // Now pad if required + while ((lcount % 4) != 0) + { + *ptr++ = XDR_encode_int8(0); + lcount++; + //cout << "0"; + } + + //cout << "\n"; + } + } + else + { + // Nothing to encode + *ptr++ = id; + *ptr++ = XDR_encode_uint32(0); + //cout << "Prop:" << (*it)->id << " " << (*it)->type << " 0\n"; + } + } + break; + + default: + //cout << " Unknown Type: " << (*it)->type << "\n"; + *ptr++ = id; + *ptr++ = XDR_encode_float((*it)->float_value);; + //cout << "Prop:" << (*it)->id << " " << (*it)->type << " "<< (*it)->float_value << "\n"; + break; + } + + ++it; + } +escape: + unsigned msgLen = reinterpret_cast(ptr) - msgBuf.Msg; + FillMsgHdr(msgBuf.msgHdr(), POS_DATA_ID, msgLen); + mSocket->sendto(msgBuf.Msg, msgLen, 0, &mServer); + SG_LOG(SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::SendMyPosition"); +} // FGMultiplayMgr::SendMyPosition() + +////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////// +// +// Name: SendTextMessage +// Description: Sends a message to the player. The message must +// contain a valid and correctly filled out header and optional +// message body. +// +////////////////////////////////////////////////////////////////////// +void +FGMultiplayMgr::SendTextMessage(const string &MsgText) +{ + 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 () +////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////// +// +// Name: ProcessData +// Description: Processes data waiting at the receive socket. The +// processing ends when there is no more data at the socket. +// +////////////////////////////////////////////////////////////////////// +void +FGMultiplayMgr::Update(void) +{ + if (!mInitialised) + return; + + /// Just for expiry + long stamp = SGTimeStamp::now().getSeconds(); + + ////////////////////////////////////////////////// + // Read the receive socket and process any data + ////////////////////////////////////////////////// + ssize_t bytes; + do { + MsgBuf msgBuf; + ////////////////////////////////////////////////// + // 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(msgBuf.Msg, sizeof(msgBuf.Msg), 0, + &SenderAddress); + ////////////////////////////////////////////////// + // no Data received + ////////////////////////////////////////////////// + if (bytes <= 0) { + if (errno != EAGAIN && errno != 0) // MSVC output "NoError" otherwise + perror("FGMultiplayMgr::MP_ProcessData"); + break; + } + if (bytes <= static_cast(sizeof(T_MsgHdr))) { + SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - " + << "received message with insufficient data" ); + break; + } + ////////////////////////////////////////////////// + // Read header + ////////////////////////////////////////////////// + T_MsgHdr* MsgHdr = msgBuf.msgHdr(); + 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); + MsgHdr->Callsign[MAX_CALLSIGN_LEN -1] = '\0'; + if (MsgHdr->Magic != MSG_MAGIC) { + SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - " + << "message has invalid magic number!" ); + break; + } + if (MsgHdr->Version != PROTO_VER) { + SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - " + << "message has invalid protocoll number!" ); + break; + } + if (MsgHdr->MsgLen != bytes) { + SG_LOG(SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - " + << "message from " << MsgHdr->Callsign << " has invalid length!"); + break; + } + ////////////////////////////////////////////////// + // Process messages + ////////////////////////////////////////////////// + switch (MsgHdr->MsgId) { + case CHAT_MSG_ID: + ProcessChatMsg(msgBuf, SenderAddress); + break; + case POS_DATA_ID: + ProcessPosMsg(msgBuf, SenderAddress, 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_DEBUG, "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) +////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////// +// +// handle a position message +// +////////////////////////////////////////////////////////////////////// +void +FGMultiplayMgr::ProcessPosMsg(const FGMultiplayMgr::MsgBuf& Msg, + const netAddress& SenderAddress, long stamp) +{ + const T_MsgHdr* MsgHdr = Msg.msgHdr(); + if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + sizeof(T_PositionMsg)) { + SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - " + << "Position message received with insufficient data" ); + return; + } + const T_PositionMsg* PosMsg = Msg.posMsg(); + 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]); + + + //cout << "INPUT MESSAGE\n"; + + // There was a bug in 1.9.0 and before: T_PositionMsg was 196 bytes + // on 32 bit architectures and 200 bytes on 64 bit, and this + // structure is put directly on the wire. By looking at the padding, + // we can sort through the mess, mostly: + // If padding is 0 (which is not a valid property type), then the + // message was produced by a new client or an old 64 bit client that + // happened to have 0 on the stack; + // Else if the property list starting with the padding word is + // well-formed, then the client is probably an old 32 bit client and + // we'll go with that; + // Else it is an old 64-bit client and properties start after the + // padding. + // There is a chance that we could be fooled by garbage in the + // padding looking like a valid property, so verifyProperties() is + // strict about the validity of the property values. + const xdr_data_t* xdr = Msg.properties(); + if (PosMsg->pad != 0) { + if (verifyProperties(&PosMsg->pad, Msg.propsRecvdEnd())) + xdr = &PosMsg->pad; + else if (!verifyProperties(xdr, Msg.propsRecvdEnd())) + goto noprops; + } + while (xdr < Msg.propsRecvdEnd()) { + FGPropertyData* pData = new FGPropertyData; + // simgear::props::Type type = simgear::props::UNSPECIFIED; + + // First element is always the ID + pData->id = XDR_decode_uint32(*xdr); + //cout << pData->id << " "; + xdr++; + + // Check the ID actually exists and get the type + const IdPropertyList* plist = findProperty(pData->id); + + if (plist) + { + pData->type = plist->type; + // How we decode the remainder of the property depends on the type + switch (pData->type) { + case simgear::props::INT: + case simgear::props::BOOL: + case simgear::props::LONG: + pData->int_value = XDR_decode_uint32(*xdr); + xdr++; + //cout << pData->int_value << "\n"; + break; + case simgear::props::FLOAT: + case simgear::props::DOUBLE: + pData->float_value = XDR_decode_float(*xdr); + xdr++; + //cout << pData->float_value << "\n"; + break; + case simgear::props::STRING: + case simgear::props::UNSPECIFIED: + { + // String is complicated. It consists of + // The length of the string + // The string itself + // Padding to the nearest 4-bytes. + uint32_t length = XDR_decode_uint32(*xdr); + xdr++; + //cout << length << " "; + // Old versions truncated the string but left the length unadjusted. + if (length > MAX_TEXT_SIZE) + length = MAX_TEXT_SIZE; + pData->string_value = new char[length + 1]; + //cout << " String: "; + for (unsigned i = 0; i < length; i++) + { + pData->string_value[i] = (char) XDR_decode_int8(*xdr); + xdr++; + //cout << pData->string_value[i]; + } + + pData->string_value[length] = '\0'; + + // Now handle the padding + while ((length % 4) != 0) + { + xdr++; + length++; + //cout << "0"; + } + //cout << "\n"; + } + break; + + default: + pData->float_value = XDR_decode_float(*xdr); + SG_LOG(SG_NETWORK, SG_DEBUG, "Unknown Prop type " << pData->id << " " << pData->type); + xdr++; + break; + } + + motionInfo.properties.push_back(pData); + } + else + { + // We failed to find the property. We'll try the next packet immediately. + SG_LOG(SG_NETWORK, SG_INFO, "FGMultiplayMgr::ProcessPosMsg - " + "message from " << MsgHdr->Callsign << " has unknown property id " + << pData->id); + } + } + noprops: + FGAIMultiplayer* mp = getMultiplayer(MsgHdr->Callsign); + if (!mp) + mp = addMultiplayer(MsgHdr->Callsign, PosMsg->Model); + mp->addMotionInfo(motionInfo, stamp); +} // FGMultiplayMgr::ProcessPosMsg() +////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////// +// +// handle a chat message +// FIXME: display chat message withi flightgear +// +////////////////////////////////////////////////////////////////////// +void +FGMultiplayMgr::ProcessChatMsg(const MsgBuf& Msg, + const netAddress& SenderAddress) +{ + const T_MsgHdr* MsgHdr = Msg.msgHdr(); + if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + 1) { + SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - " + << "Chat message received with insufficient data" ); + return; + } + + char *chatStr = new char[MsgHdr->MsgLen - sizeof(T_MsgHdr)]; + const T_ChatMsg* ChatMsg + = reinterpret_cast(Msg.Msg + sizeof(T_MsgHdr)); + strncpy(chatStr, ChatMsg->Text, + MsgHdr->MsgLen - sizeof(T_MsgHdr)); + chatStr[MsgHdr->MsgLen - sizeof(T_MsgHdr) - 1] = '\0'; + + SG_LOG (SG_NETWORK, SG_WARN, "Chat [" << MsgHdr->Callsign << "]" + << " " << chatStr); + + delete [] chatStr; +} // FGMultiplayMgr::ProcessChatMsg () +////////////////////////////////////////////////////////////////////// + +void +FGMultiplayMgr::FillMsgHdr(T_MsgHdr *MsgHdr, int MsgId, unsigned _len) +{ + 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'; +} + +FGAIMultiplayer* +FGMultiplayMgr::addMultiplayer(const std::string& callsign, + const std::string& modelName) +{ + if (0 < mMultiPlayerMap.count(callsign)) + return mMultiPlayerMap[callsign].get(); + + 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 ... + for (unsigned i = 0; i < numProperties; ++i) + mp->addPropertyId(sIdPropertyList[i].id, sIdPropertyList[i].name); + } + + return mp; +} + +FGAIMultiplayer* +FGMultiplayMgr::getMultiplayer(const std::string& callsign) +{ + if (0 < mMultiPlayerMap.count(callsign)) + return mMultiPlayerMap[callsign].get(); + else + return 0; +} From 558582b58d8c9d4aad1f389919dcd6428ba770df Mon Sep 17 00:00:00 2001 From: Durk Talsma Date: Fri, 3 Sep 2010 10:47:05 +0200 Subject: [PATCH 3/5] * A few new features to allow database management and traffic pattern developement: --prop:/sim/traffic-manager/dumpdata=true: dumps the currently loaded datafile to the console for inspection. --prop:/sim/traffic-manager/datafile=/path/to/somefile.xml: Skips autoscanning and reads traffic only from the specified xml file. --prop:/sim/traffic-manager/datafile=/path/to/somefile.conf: Slips autoscanning and reads traffic from a plain text file, as described on the forum/wiki * Removed the use of pointers for heuristics calculations, as urged by Frederic Bouvier * Fixed domain error in proportion calculation during loading, resulting in random skipping of aircraft, as reported by Jorg van der Venne. --- src/Traffic/Schedule.cxx | 5 +- src/Traffic/TrafficMgr.cxx | 689 ++++++++++++++++++++----------------- src/Traffic/TrafficMgr.hxx | 12 +- 3 files changed, 377 insertions(+), 329 deletions(-) diff --git a/src/Traffic/Schedule.cxx b/src/Traffic/Schedule.cxx index e86cbcf24..3b8e7636d 100644 --- a/src/Traffic/Schedule.cxx +++ b/src/Traffic/Schedule.cxx @@ -364,10 +364,12 @@ void FGAISchedule::scheduleFlights() currentDestination = flight->getArrivalAirport()->getId(); if (!initialized) { string departurePort = flight->getDepartureAirport()->getId(); + //cerr << "Scheduled " << registration << " " << score << " for Flight " + // << flight-> getCallSign() << " from " << departurePort << " to " << currentDestination << endl; if (fgGetString("/sim/presets/airport-id") == departurePort) { hits++; } - runCount++; + //runCount++; initialized = true; } @@ -508,6 +510,7 @@ void FGAISchedule::setScore () score = 0.0; } } + runCount++; } bool compareSchedules(FGAISchedule*a, FGAISchedule*b) diff --git a/src/Traffic/TrafficMgr.cxx b/src/Traffic/TrafficMgr.cxx index c570c9ebd..5e649472c 100644 --- a/src/Traffic/TrafficMgr.cxx +++ b/src/Traffic/TrafficMgr.cxx @@ -18,7 +18,7 @@ * * **************************************************************************/ - + /* * Traffic manager parses airlines timetable-like data and uses this to * determine the approximate position of each AI aircraft in its database. @@ -72,18 +72,18 @@ using std::sort; using std::strcmp; - + /****************************************************************************** * TrafficManager *****************************************************************************/ FGTrafficManager::FGTrafficManager() { - //score = 0; - //runCount = 0; - acCounter = 0; + //score = 0; + //runCount = 0; + acCounter = 0; } -FGTrafficManager:: ~FGTrafficManager() +FGTrafficManager::~FGTrafficManager() { // Save the heuristics data bool saveData = false; @@ -92,11 +92,11 @@ FGTrafficManager:: ~FGTrafficManager() SGPath cacheData(fgGetString("/sim/fg-home")); cacheData.append("ai"); string airport = fgGetString("/sim/presets/airport-id"); - - if ((airport) != "") { + + if ((airport) != "") { char buffer[128]; ::snprintf(buffer, 128, "%c/%c/%c/", - airport[0], airport[1], airport[2]); + airport[0], airport[1], airport[2]); cacheData.append(buffer); if (!cacheData.exists()) { cacheData.create_dir(0777); @@ -106,60 +106,80 @@ FGTrafficManager:: ~FGTrafficManager() saveData = true; cachefile.open(cacheData.str().c_str()); } - } - for (ScheduleVectorIterator sched = scheduledAircraft.begin(); sched != scheduledAircraft.end(); sched++) { - if (saveData) { - cachefile << (*sched)->getRegistration() << " " - << (*sched)-> getRunCount() << " " - << (*sched)->getHits() << endl; - } - delete (*sched); } - if (saveData) { - cachefile.close(); - } - scheduledAircraft.clear(); - flights.clear(); + for (ScheduleVectorIterator sched = scheduledAircraft.begin(); + sched != scheduledAircraft.end(); sched++) { + if (saveData) { + cachefile << (*sched)->getRegistration() << " " + << (*sched)->getRunCount() << " " + << (*sched)->getHits() << endl; + } + delete(*sched); + } + if (saveData) { + cachefile.close(); + } + scheduledAircraft.clear(); + flights.clear(); } void FGTrafficManager::init() -{ - ulDir* d, *d2; - ulDirEnt* dent, *dent2; - SGPath aircraftDir = globals->get_fg_root(); - SGPath path = aircraftDir; - heuristicsVector heuristics; - HeuristicMap heurMap; - - - aircraftDir.append("AI/Traffic"); - if ((d = ulOpenDir(aircraftDir.c_str())) != NULL) - { - while((dent = ulReadDir(d)) != NULL) { - if (string(dent->d_name) != string(".") && - string(dent->d_name) != string("..") && - dent->d_isdir) - { - SGPath currACDir = aircraftDir; - currACDir.append(dent->d_name); - if ((d2 = ulOpenDir(currACDir.c_str())) == NULL) - return; - while ((dent2 = ulReadDir(d2)) != NULL) { - SGPath currFile = currACDir; - currFile.append(dent2->d_name); - if (currFile.extension() == string("xml")) - { - SGPath currFile = currACDir; - currFile.append(dent2->d_name); - SG_LOG(SG_GENERAL, SG_DEBUG, "Scanning " << currFile.str() << " for traffic"); - readXML(currFile.str(),*this); - } - } - ulCloseDir(d2); - } - } - ulCloseDir(d); +{ + ulDir *d, *d2; + ulDirEnt *dent, *dent2; + + heuristicsVector heuristics; + HeuristicMap heurMap; + + if (string(fgGetString("/sim/traffic-manager/datafile")) == string("")) { + SGPath aircraftDir = globals->get_fg_root(); + SGPath path = aircraftDir; + + aircraftDir.append("AI/Traffic"); + if ((d = ulOpenDir(aircraftDir.c_str())) != NULL) { + while ((dent = ulReadDir(d)) != NULL) { + if (string(dent->d_name) != string(".") && + string(dent->d_name) != string("..") && dent->d_isdir) { + SGPath currACDir = aircraftDir; + currACDir.append(dent->d_name); + if ((d2 = ulOpenDir(currACDir.c_str())) == NULL) + return; + while ((dent2 = ulReadDir(d2)) != NULL) { + SGPath currFile = currACDir; + currFile.append(dent2->d_name); + if (currFile.extension() == string("xml")) { + SGPath currFile = currACDir; + currFile.append(dent2->d_name); + SG_LOG(SG_GENERAL, SG_DEBUG, + "Scanning " << currFile. + str() << " for traffic"); + readXML(currFile.str(), *this); + } + } + ulCloseDir(d2); + } + } + ulCloseDir(d); + } + } else { + fgSetBool("/sim/traffic-manager/heuristics", false); + SGPath path = string(fgGetString("/sim/traffic-manager/datafile")); + string ext = path.extension(); + if (path.extension() == "xml") { + if (path.exists()) { + readXML(path.str(), *this); + } + } else if (path.extension() == "conf") { + if (path.exists()) { + readTimeTableFromFile(path); + } + } else { + SG_LOG(SG_GENERAL, SG_ALERT, + "Unknown data format " << path.str() + << " for traffic"); + } + //exit(1); } if (fgGetBool("/sim/traffic-manager/heuristics")) { //cerr << "Processing Heuristics" << endl; @@ -170,97 +190,98 @@ void FGTrafficManager::init() if ((airport) != "") { char buffer[128]; ::snprintf(buffer, 128, "%c/%c/%c/", - airport[0], airport[1], airport[2]); + airport[0], airport[1], airport[2]); cacheData.append(buffer); cacheData.append(airport + "-cache.txt"); if (cacheData.exists()) { ifstream data(cacheData.c_str()); while (1) { - Heuristic *h = new Heuristic; - data >> h->registration >> h->runCount >> h->hits; + Heuristic h; // = new Heuristic; + data >> h.registration >> h.runCount >> h.hits; if (data.eof()) break; - heurMap[h->registration] = h; + heurMap[h.registration] = h; heuristics.push_back(h); } } } - for (currAircraft = scheduledAircraft.begin(); - currAircraft != scheduledAircraft.end(); - currAircraft++) { + for (currAircraft = scheduledAircraft.begin(); + currAircraft != scheduledAircraft.end(); currAircraft++) { string registration = (*currAircraft)->getRegistration(); HeuristicMapIterator itr = heurMap.find(registration); //cerr << "Processing heuristics for" << (*currAircraft)->getRegistration() << endl; if (itr == heurMap.end()) { //cerr << "No heuristics found for " << registration << endl; } else { - (*currAircraft)->setrunCount(itr->second->runCount); - (*currAircraft)->setHits (itr->second->hits); - (*currAircraft)->setScore(); + (*currAircraft)->setrunCount(itr->second.runCount); + (*currAircraft)->setHits(itr->second.hits); //cerr <<"Runcount " << itr->second->runCount << ".Hits " << itr->second->hits << endl; } } //cerr << "Done" << endl; - for (heuristicsVectorIterator hvi = heuristics.begin(); - hvi != heuristics.end(); - hvi++) { - delete (*hvi); - } - sort (scheduledAircraft.begin(), scheduledAircraft.end(), compareSchedules); + //for (heuristicsVectorIterator hvi = heuristics.begin(); + // hvi != heuristics.end(); hvi++) { + // delete(*hvi); + //} } + // Do sorting and scoring separately, to take advantage of the "homeport| variable + for (currAircraft = scheduledAircraft.begin(); + currAircraft != scheduledAircraft.end(); currAircraft++) { + (*currAircraft)->setScore(); + } + sort(scheduledAircraft.begin(), scheduledAircraft.end(), + compareSchedules); currAircraft = scheduledAircraft.begin(); currAircraftClosest = scheduledAircraft.begin(); } -void FGTrafficManager::update(double /*dt*/) +void FGTrafficManager::update(double /*dt */ ) { - if (fgGetBool("/environment/metar/valid") == false) { - return; - } - time_t now = time(NULL) + fgGetLong("/sim/time/warp"); - if (scheduledAircraft.size() == 0) { - return; - } - - SGVec3d userCart = SGVec3d::fromGeod(SGGeod::fromDeg( - fgGetDouble("/position/longitude-deg"), - fgGetDouble("/position/latitude-deg"))); - - if(currAircraft == scheduledAircraft.end()) - { - currAircraft = scheduledAircraft.begin(); + if (fgGetBool("/environment/metar/valid") == false) { + return; } - //cerr << "Processing << " << (*currAircraft)->getRegistration() << " with score " << (*currAircraft)->getScore() << endl; - if (!((*currAircraft)->update(now, userCart))) - { - // NOTE: With traffic manager II, this statement below is no longer true - // after proper initialization, we shouldnt get here. - // But let's make sure - //SG_LOG( SG_GENERAL, SG_ALERT, "Failed to update aircraft schedule in traffic manager"); + time_t now = time(NULL) + fgGetLong("/sim/time/warp"); + if (scheduledAircraft.size() == 0) { + return; } - currAircraft++; + + SGVec3d userCart = + SGVec3d::fromGeod(SGGeod:: + fromDeg(fgGetDouble("/position/longitude-deg"), + fgGetDouble("/position/latitude-deg"))); + + if (currAircraft == scheduledAircraft.end()) { + currAircraft = scheduledAircraft.begin(); + } + //cerr << "Processing << " << (*currAircraft)->getRegistration() << " with score " << (*currAircraft)->getScore() << endl; + if (!((*currAircraft)->update(now, userCart))) { + // NOTE: With traffic manager II, this statement below is no longer true + // after proper initialization, we shouldnt get here. + // But let's make sure + //SG_LOG( SG_GENERAL, SG_ALERT, "Failed to update aircraft schedule in traffic manager"); + } + currAircraft++; } void FGTrafficManager::release(int id) { - releaseList.push_back(id); + releaseList.push_back(id); } bool FGTrafficManager::isReleased(int id) { - IdListIterator i = releaseList.begin(); - while (i != releaseList.end()) - { - if ((*i) == id) - { - releaseList.erase(i); - return true; - } - i++; + IdListIterator i = releaseList.begin(); + while (i != releaseList.end()) { + if ((*i) == id) { + releaseList.erase(i); + return true; + } + i++; } - return false; + return false; } -/* + + void FGTrafficManager::readTimeTableFromFile(SGPath infileName) { string model; @@ -316,9 +337,10 @@ void FGTrafficManager::readTimeTableFromFile(SGPath infileName) FlightType = tokens[9]; radius = atof(tokens[8].c_str()); offset = atof(tokens[7].c_str());; - //cerr << "Found AC string " << model << " " << livery << " " << homePort << " " - // << registration << " " << flightReq << " " << isHeavy << " " << acType << " " << airline << " " << m_class - // << " " << FlightType << " " << radius << " " << offset << endl; + SG_LOG(SG_GENERAL, SG_ALERT, "Adding Aircraft" << model << " " << livery << " " << homePort << " " + << registration << " " << flightReq << " " << isHeavy + << " " << acType << " " << airline << " " << m_class + << " " << FlightType << " " << radius << " " << offset); scheduledAircraft.push_back(new FGAISchedule(model, livery, homePort, @@ -350,7 +372,7 @@ void FGTrafficManager::readTimeTableFromFile(SGPath infileName) string requiredAircraft = tokens[9]; if (weekdays.size() != 7) { - cerr << "Found misconfigured weekdays string" << weekdays << endl; + SG_LOG(SG_GENERAL, SG_ALERT, "Found misconfigured weekdays string" << weekdays); exit(1); } depTime.clear(); @@ -365,31 +387,32 @@ void FGTrafficManager::readTimeTableFromFile(SGPath infileName) arrivalWeekdayNeedsIncrement = true; } for (int i = 0; i < 7; i++) { + int j = i+1; if (weekdays[i] != '.') { char buffer[4]; - snprintf(buffer, 4, "%d/", i); + snprintf(buffer, 4, "%d/", j); string departureTime = string(buffer) + depTimeGen + string(":00"); string arrivalTime; if (!arrivalWeekdayNeedsIncrement) { arrivalTime = string(buffer) + arrTimeGen + string(":00"); } if (arrivalWeekdayNeedsIncrement && i != 6 ) { - snprintf(buffer, 4, "%d/", i+1); + snprintf(buffer, 4, "%d/", j+1); arrivalTime = string(buffer) + arrTimeGen + string(":00"); } if (arrivalWeekdayNeedsIncrement && i == 6 ) { snprintf(buffer, 4, "%d/", 0); arrivalTime = string(buffer) + arrTimeGen + string(":00"); } - cerr << "Adding flight: " << callsign << " " - << fltrules << " " - << departurePort << " " - << arrivalPort << " " - << cruiseAlt << " " - << departureTime << " " - << arrivalTime << " " - << repeat << " " - << requiredAircraft << endl; + SG_LOG(SG_GENERAL, SG_ALERT, "Adding flight " << callsign << " " + << fltrules << " " + << departurePort << " " + << arrivalPort << " " + << cruiseAlt << " " + << departureTime << " " + << arrivalTime << " " + << repeat << " " + << requiredAircraft); flights[requiredAircraft].push_back(new FGScheduledFlight(callsign, fltrules, @@ -407,9 +430,9 @@ void FGTrafficManager::readTimeTableFromFile(SGPath infileName) } //exit(1); -}*/ +} + -/* void FGTrafficManager::Tokenize(const string& str, vector& tokens, const string& delimiters) @@ -429,213 +452,235 @@ void FGTrafficManager::Tokenize(const string& str, pos = str.find_first_of(delimiters, lastPos); } } -*/ -void FGTrafficManager::startXML () { - //cout << "Start XML" << endl; - requiredAircraft = ""; - homePort = ""; -} -void FGTrafficManager::endXML () { - //cout << "End XML" << endl; -} - -void FGTrafficManager::startElement (const char * name, const XMLAttributes &atts) { - const char * attval; - //cout << "Start element " << name << endl; - //FGTrafficManager temp; - //for (int i = 0; i < atts.size(); i++) - // if (string(atts.getName(i)) == string("include")) - attval = atts.getValue("include"); - if (attval != 0) - { - //cout << "including " << attval << endl; - SGPath path = - globals->get_fg_root(); - path.append("/Traffic/"); - path.append(attval); - readXML(path.str(), *this); - } - elementValueStack.push_back( "" ); - // cout << " " << atts.getName(i) << '=' << atts.getValue(i) << endl; -} - -void FGTrafficManager::endElement (const char * name) { - //cout << "End element " << name << endl; - const string& value = elementValueStack.back(); - - if (!strcmp(name, "model")) - mdl = value; - else if (!strcmp(name, "livery")) - livery = value; - else if (!strcmp(name, "home-port")) - homePort = value; - else if (!strcmp(name, "registration")) - registration = value; - else if (!strcmp(name, "airline")) - airline = value; - else if (!strcmp(name, "actype")) - acType = value; - else if (!strcmp(name, "required-aircraft")) - requiredAircraft = value; - else if (!strcmp(name, "flighttype")) - flighttype = value; - else if (!strcmp(name, "radius")) - radius = atoi(value.c_str()); - else if (!strcmp(name, "offset")) - offset = atoi(value.c_str()); - else if (!strcmp(name, "performance-class")) - m_class = value; - else if (!strcmp(name, "heavy")) - { - if(value == string("true")) - heavy = true; - else - heavy = false; - } - else if (!strcmp(name, "callsign")) - callsign = value; - else if (!strcmp(name, "fltrules")) - fltrules = value; - else if (!strcmp(name, "port")) - port = value; - else if (!strcmp(name, "time")) - timeString = value; - else if (!strcmp(name, "departure")) - { - departurePort = port; - departureTime = timeString; - } - else if (!strcmp(name, "cruise-alt")) - cruiseAlt = atoi(value.c_str()); - else if (!strcmp(name, "arrival")) - { - arrivalPort = port; - arrivalTime = timeString; - } - else if (!strcmp(name, "repeat")) - repeat = value; - else if (!strcmp(name, "flight")) - { - // We have loaded and parsed all the information belonging to this flight - // so we temporarily store it. - //cerr << "Pusing back flight " << callsign << endl; - //cerr << callsign << " " << fltrules << " "<< departurePort << " " << arrivalPort << " " - // << cruiseAlt << " " << departureTime<< " "<< arrivalTime << " " << repeat << endl; - - //Prioritize aircraft - string apt = fgGetString("/sim/presets/airport-id"); - //cerr << "Airport information: " << apt << " " << departurePort << " " << arrivalPort << endl; - //if (departurePort == apt) score++; - //flights.push_back(new FGScheduledFlight(callsign, - // fltrules, - // departurePort, - // arrivalPort, - // cruiseAlt, - // departureTime, - // arrivalTime, - // repeat)); - if (requiredAircraft == "") { - char buffer[16]; - snprintf(buffer, 16, "%d", acCounter); - requiredAircraft = buffer; - } - SG_LOG(SG_GENERAL, SG_DEBUG, "Adding flight: " << callsign << " " - << fltrules << " " - << departurePort << " " - << arrivalPort << " " - << cruiseAlt << " " - << departureTime << " " - << arrivalTime << " " - << repeat << " " - << requiredAircraft); - - flights[requiredAircraft].push_back(new FGScheduledFlight(callsign, - fltrules, - departurePort, - arrivalPort, - cruiseAlt, - departureTime, - arrivalTime, - repeat, - requiredAircraft)); - requiredAircraft = ""; - } - else if (!strcmp(name, "aircraft")) - { - int proportion = (int) (fgGetDouble("/sim/traffic-manager/proportion") * 100); - int randval = rand() & 100; - if (randval < proportion) { - //scheduledAircraft.push_back(new FGAISchedule(mdl, - // livery, - // registration, - // heavy, - // acType, - // airline, - // m_class, - // flighttype, - // radius, - // offset, - // score, - // flights)); - if (requiredAircraft == "") { - char buffer[16]; - snprintf(buffer, 16, "%d", acCounter); - requiredAircraft = buffer; - } - if (homePort == "") { - homePort = departurePort; - } - scheduledAircraft.push_back(new FGAISchedule(mdl, - livery, - homePort, - registration, - requiredAircraft, - heavy, - acType, - airline, - m_class, - flighttype, - radius, - offset)); - - // while(flights.begin() != flights.end()) { -// flights.pop_back(); -// } - } - acCounter++; +void FGTrafficManager::startXML() +{ + //cout << "Start XML" << endl; requiredAircraft = ""; homePort = ""; - //for (FGScheduledFlightVecIterator flt = flights.begin(); flt != flights.end(); flt++) - // { - // delete (*flt); - // } - //flights.clear(); - SG_LOG( SG_GENERAL, SG_BULK, "Reading aircraft : " - << registration - << " with prioritization score " - << score); - score = 0; +} + +void FGTrafficManager::endXML() +{ + //cout << "End XML" << endl; +} + +void FGTrafficManager::startElement(const char *name, + const XMLAttributes & atts) +{ + const char *attval; + //cout << "Start element " << name << endl; + //FGTrafficManager temp; + //for (int i = 0; i < atts.size(); i++) + // if (string(atts.getName(i)) == string("include")) + attval = atts.getValue("include"); + if (attval != 0) { + //cout << "including " << attval << endl; + SGPath path = globals->get_fg_root(); + path.append("/Traffic/"); + path.append(attval); + readXML(path.str(), *this); } - elementValueStack.pop_back(); + elementValueStack.push_back(""); + // cout << " " << atts.getName(i) << '=' << atts.getValue(i) << endl; } -void FGTrafficManager::data (const char * s, int len) { - string token = string(s,len); - //cout << "Character data " << string(s,len) << endl; - elementValueStack.back() += token; +void FGTrafficManager::endElement(const char *name) +{ + //cout << "End element " << name << endl; + const string & value = elementValueStack.back(); + + if (!strcmp(name, "model")) + mdl = value; + else if (!strcmp(name, "livery")) + livery = value; + else if (!strcmp(name, "home-port")) + homePort = value; + else if (!strcmp(name, "registration")) + registration = value; + else if (!strcmp(name, "airline")) + airline = value; + else if (!strcmp(name, "actype")) + acType = value; + else if (!strcmp(name, "required-aircraft")) + requiredAircraft = value; + else if (!strcmp(name, "flighttype")) + flighttype = value; + else if (!strcmp(name, "radius")) + radius = atoi(value.c_str()); + else if (!strcmp(name, "offset")) + offset = atoi(value.c_str()); + else if (!strcmp(name, "performance-class")) + m_class = value; + else if (!strcmp(name, "heavy")) { + if (value == string("true")) + heavy = true; + else + heavy = false; + } else if (!strcmp(name, "callsign")) + callsign = value; + else if (!strcmp(name, "fltrules")) + fltrules = value; + else if (!strcmp(name, "port")) + port = value; + else if (!strcmp(name, "time")) + timeString = value; + else if (!strcmp(name, "departure")) { + departurePort = port; + departureTime = timeString; + } else if (!strcmp(name, "cruise-alt")) + cruiseAlt = atoi(value.c_str()); + else if (!strcmp(name, "arrival")) { + arrivalPort = port; + arrivalTime = timeString; + } else if (!strcmp(name, "repeat")) + repeat = value; + else if (!strcmp(name, "flight")) { + // We have loaded and parsed all the information belonging to this flight + // so we temporarily store it. + //cerr << "Pusing back flight " << callsign << endl; + //cerr << callsign << " " << fltrules << " "<< departurePort << " " << arrivalPort << " " + // << cruiseAlt << " " << departureTime<< " "<< arrivalTime << " " << repeat << endl; + + //Prioritize aircraft + string apt = fgGetString("/sim/presets/airport-id"); + //cerr << "Airport information: " << apt << " " << departurePort << " " << arrivalPort << endl; + //if (departurePort == apt) score++; + //flights.push_back(new FGScheduledFlight(callsign, + // fltrules, + // departurePort, + // arrivalPort, + // cruiseAlt, + // departureTime, + // arrivalTime, + // repeat)); + if (requiredAircraft == "") { + char buffer[16]; + snprintf(buffer, 16, "%d", acCounter); + requiredAircraft = buffer; + } + SG_LOG(SG_GENERAL, SG_DEBUG, "Adding flight: " << callsign << " " + << fltrules << " " + << departurePort << " " + << arrivalPort << " " + << cruiseAlt << " " + << departureTime << " " + << arrivalTime << " " << repeat << " " << requiredAircraft); + // For database maintainance purposes, it may be convenient to + // + if (fgGetBool("/sim/traffic-manager/dumpdata") == true) { + SG_LOG(SG_GENERAL, SG_ALERT, "Traffic Dump FLIGHT," << callsign << "," + << fltrules << "," + << departurePort << "," + << arrivalPort << "," + << cruiseAlt << "," + << departureTime << "," + << arrivalTime << "," << repeat << "," << requiredAircraft); + } + flights[requiredAircraft].push_back(new FGScheduledFlight(callsign, + fltrules, + departurePort, + arrivalPort, + cruiseAlt, + departureTime, + arrivalTime, + repeat, + requiredAircraft)); + requiredAircraft = ""; + } else if (!strcmp(name, "aircraft")) { + string isHeavy; + if (heavy) { + isHeavy = "true"; + } else { + isHeavy = "false"; + } + /* + cerr << "Traffic Dump AC," << homePort << "," << registration << "," << requiredAircraft + << "," << acType << "," << livery << "," + << airline << "," << offset << "," << radius << "," << flighttype << "," << isHeavy << "," << mdl << endl;*/ + int proportion = + (int) (fgGetDouble("/sim/traffic-manager/proportion") * 100); + int randval = rand() & 100; + if (randval <= proportion) { + if (fgGetBool("/sim/traffic-manager/dumpdata") == true) { + SG_LOG(SG_GENERAL, SG_ALERT, "Traffic Dump AC," << homePort << "," << registration << "," << requiredAircraft + << "," << acType << "," << livery << "," + << airline << "," << offset << "," << radius << "," << flighttype << "," << isHeavy << "," << mdl); + } + //scheduledAircraft.push_back(new FGAISchedule(mdl, + // livery, + // registration, + // heavy, + // acType, + // airline, + // m_class, + // flighttype, + // radius, + // offset, + // score, + // flights)); + if (requiredAircraft == "") { + char buffer[16]; + snprintf(buffer, 16, "%d", acCounter); + requiredAircraft = buffer; + } + if (homePort == "") { + homePort = departurePort; + } + scheduledAircraft.push_back(new FGAISchedule(mdl, + livery, + homePort, + registration, + requiredAircraft, + heavy, + acType, + airline, + m_class, + flighttype, + radius, offset)); + + // while(flights.begin() != flights.end()) { +// flights.pop_back(); +// } + } else { + cerr << "Skipping : " << randval; + } + acCounter++; + requiredAircraft = ""; + homePort = ""; + //for (FGScheduledFlightVecIterator flt = flights.begin(); flt != flights.end(); flt++) + // { + // delete (*flt); + // } + //flights.clear(); + SG_LOG(SG_GENERAL, SG_BULK, "Reading aircraft : " + << registration << " with prioritization score " << score); + score = 0; + } + elementValueStack.pop_back(); } -void FGTrafficManager::pi (const char * target, const char * data) { - //cout << "Processing instruction " << target << ' ' << data << endl; +void FGTrafficManager::data(const char *s, int len) +{ + string token = string(s, len); + //cout << "Character data " << string(s,len) << endl; + elementValueStack.back() += token; } -void FGTrafficManager::warning (const char * message, int line, int column) { - SG_LOG(SG_IO, SG_WARN, "Warning: " << message << " (" << line << ',' << column << ')'); +void FGTrafficManager::pi(const char *target, const char *data) +{ + //cout << "Processing instruction " << target << ' ' << data << endl; } -void FGTrafficManager::error (const char * message, int line, int column) { - SG_LOG(SG_IO, SG_ALERT, "Error: " << message << " (" << line << ',' << column << ')'); +void FGTrafficManager::warning(const char *message, int line, int column) +{ + SG_LOG(SG_IO, SG_WARN, + "Warning: " << message << " (" << line << ',' << column << ')'); } +void FGTrafficManager::error(const char *message, int line, int column) +{ + SG_LOG(SG_IO, SG_ALERT, + "Error: " << message << " (" << line << ',' << column << ')'); +} diff --git a/src/Traffic/TrafficMgr.hxx b/src/Traffic/TrafficMgr.hxx index 6c72ce95c..862c87be4 100644 --- a/src/Traffic/TrafficMgr.hxx +++ b/src/Traffic/TrafficMgr.hxx @@ -65,11 +65,11 @@ public: unsigned int hits; }; -typedef vector heuristicsVector; -typedef vector::iterator heuristicsVectorIterator; +typedef vector heuristicsVector; +typedef vector::iterator heuristicsVectorIterator; -typedef std::map < std::string, Heuristic *> HeuristicMap; -typedef HeuristicMap::iterator HeuristicMapIterator; +typedef std::map < std::string, Heuristic> HeuristicMap; +typedef HeuristicMap::iterator HeuristicMapIterator; @@ -93,8 +93,8 @@ private: FGScheduledFlightMap flights; - //void readTimeTableFromFile(SGPath infilename); - //void Tokenize(const string& str, vector& tokens, const string& delimiters = " "); + void readTimeTableFromFile(SGPath infilename); + void Tokenize(const string& str, vector& tokens, const string& delimiters = " "); public: FGTrafficManager(); From aba57077fd690e7386913d311b789b53e5dae237 Mon Sep 17 00:00:00 2001 From: Durk Talsma Date: Fri, 3 Sep 2010 12:32:55 +0200 Subject: [PATCH 4/5] Issue a warning when finding duplicate tail numbers. Note that this is far from a fatal error, but does interfere with the startup heuristics data collection mechanism. --- src/Traffic/TrafficMgr.cxx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Traffic/TrafficMgr.cxx b/src/Traffic/TrafficMgr.cxx index 5e649472c..f25516a26 100644 --- a/src/Traffic/TrafficMgr.cxx +++ b/src/Traffic/TrafficMgr.cxx @@ -200,6 +200,11 @@ void FGTrafficManager::init() data >> h.registration >> h.runCount >> h.hits; if (data.eof()) break; + HeuristicMapIterator itr = heurMap.find(h.registration); + if (itr != heurMap.end()) { + SG_LOG(SG_GENERAL, SG_WARN,"Traffic Manager Warning: found duplicate tailnumber " << + h.registration << " for AI aircraft"); + } heurMap[h.registration] = h; heuristics.push_back(h); } From 1e43675f61c1e182ecd7a07535abf6d9c8caadbb Mon Sep 17 00:00:00 2001 From: Torsten Dreyer Date: Sun, 5 Sep 2010 11:56:38 +0200 Subject: [PATCH 5/5] Expose some OSG DisplaySettings to the property tree --- src/Main/fg_os_osgviewer.cxx | 85 ++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/src/Main/fg_os_osgviewer.cxx b/src/Main/fg_os_osgviewer.cxx index a241e60c3..a03cc747e 100644 --- a/src/Main/fg_os_osgviewer.cxx +++ b/src/Main/fg_os_osgviewer.cxx @@ -101,6 +101,80 @@ using namespace osg; static osg::ref_ptr viewer; static osg::ref_ptr mainCamera; +static void setStereoMode( const char * mode ) +{ + DisplaySettings::StereoMode stereoMode; + bool stereoOn = true; + + if (strcmp(mode,"QUAD_BUFFER")==0) + { + stereoMode = DisplaySettings::QUAD_BUFFER; + } + else if (strcmp(mode,"ANAGLYPHIC")==0) + { + stereoMode = DisplaySettings::ANAGLYPHIC; + } + else if (strcmp(mode,"HORIZONTAL_SPLIT")==0) + { + stereoMode = DisplaySettings::HORIZONTAL_SPLIT; + } + else if (strcmp(mode,"VERTICAL_SPLIT")==0) + { + stereoMode = DisplaySettings::VERTICAL_SPLIT; + } + else if (strcmp(mode,"LEFT_EYE")==0) + { + stereoMode = DisplaySettings::LEFT_EYE; + } + else if (strcmp(mode,"RIGHT_EYE")==0) + { + stereoMode = DisplaySettings::RIGHT_EYE; + } + else if (strcmp(mode,"HORIZONTAL_INTERLACE")==0) + { + stereoMode = DisplaySettings::HORIZONTAL_INTERLACE; + } + else if (strcmp(mode,"VERTICAL_INTERLACE")==0) + { + stereoMode = DisplaySettings::VERTICAL_INTERLACE; + } + else if (strcmp(mode,"CHECKERBOARD")==0) + { + stereoMode = DisplaySettings::CHECKERBOARD; + } else { + stereoOn = false; + } + DisplaySettings::instance()->setStereo( stereoOn ); + DisplaySettings::instance()->setStereoMode( stereoMode ); +} + +static const char * getStereoMode() +{ + DisplaySettings::StereoMode stereoMode = DisplaySettings::instance()->getStereoMode(); + bool stereoOn = DisplaySettings::instance()->getStereo(); + if( !stereoOn ) return "OFF"; + if( stereoMode == DisplaySettings::QUAD_BUFFER ) { + return "QUAD_BUFFER"; + } else if( stereoMode == DisplaySettings::ANAGLYPHIC ) { + return "ANAGLYPHIC"; + } else if( stereoMode == DisplaySettings::HORIZONTAL_SPLIT ) { + return "HORIZONTAL_SPLIT"; + } else if( stereoMode == DisplaySettings::VERTICAL_SPLIT ) { + return "VERTICAL_SPLIT"; + } else if( stereoMode == DisplaySettings::LEFT_EYE ) { + return "LEFT_EYE"; + } else if( stereoMode == DisplaySettings::RIGHT_EYE ) { + return "RIGHT_EYE"; + } else if( stereoMode == DisplaySettings::HORIZONTAL_INTERLACE ) { + return "HORIZONTAL_INTERLACE"; + } else if( stereoMode == DisplaySettings::VERTICAL_INTERLACE ) { + return "VERTICAL_INTERLACE"; + } else if( stereoMode == DisplaySettings::CHECKERBOARD ) { + return "CHECKERBOARD"; + } + return "OFF"; +} + void fgOSOpenWindow(bool stencil) { viewer = new osgViewer::Viewer; @@ -175,8 +249,19 @@ void fgOSOpenWindow(bool stencil) viewer->setSceneData(new osg::Group); globals->get_renderer()->setViewer(viewer.get()); CameraGroup::setDefault(cameraGroup); + + DisplaySettings * displaySettings = DisplaySettings::instance(); + fgTie("/sim/rendering/osg-displaysettings/eye-separation", displaySettings, &DisplaySettings::getEyeSeparation, &DisplaySettings::setEyeSeparation ); + fgTie("/sim/rendering/osg-displaysettings/screen-distance", displaySettings, &DisplaySettings::getScreenDistance, &DisplaySettings::setScreenDistance ); + fgTie("/sim/rendering/osg-displaysettings/screen-width", displaySettings, &DisplaySettings::getScreenWidth, &DisplaySettings::setScreenWidth ); + fgTie("/sim/rendering/osg-displaysettings/screen-height", displaySettings, &DisplaySettings::getScreenHeight, &DisplaySettings::setScreenHeight ); + fgTie("/sim/rendering/osg-displaysettings/stereo-mode", getStereoMode, setStereoMode ); + fgTie("/sim/rendering/osg-displaysettings/double-buffer", displaySettings, &DisplaySettings::getDoubleBuffer, &DisplaySettings::setDoubleBuffer ); + fgTie("/sim/rendering/osg-displaysettings/depth-buffer", displaySettings, &DisplaySettings::getDepthBuffer, &DisplaySettings::setDepthBuffer ); + fgTie("/sim/rendering/osg-displaysettings/rgb", displaySettings, &DisplaySettings::getRGB, &DisplaySettings::setRGB ); } + static int status = 0; void fgOSExit(int code)