From e43fe8209441fd9345e5ff2629d9a9c269ce11ba Mon Sep 17 00:00:00 2001 From: Stuart Buchanan Date: Fri, 27 Jul 2018 19:51:37 +0100 Subject: [PATCH] Fallback AI model implementation Aircraft can now set a /sim/model/fallback-model-index property that is transmitted over the MP network. Receiving clients use this as an index into AI/Aircraft/fallback_models.xml to determine a model to use if the model path (/sim/model/path) cannot be found under Aircraft or AI/Aircraft. This allows aircraft developers to identify a suitable fallback model to be used for those who do not have their aircraft installed. --- src/AIModel/AIBase.cxx | 48 ++++++++--- src/AIModel/AIBase.hxx | 13 ++- src/AIModel/AIManager.cxx | 4 +- src/AIModel/AIManager.hxx | 2 - src/AIModel/AIMultiplayer.cxx | 25 +++--- src/MultiPlayer/multiplaymgr.cxx | 136 +++++++++++++++++-------------- src/MultiPlayer/multiplaymgr.hxx | 26 +++--- 7 files changed, 149 insertions(+), 105 deletions(-) diff --git a/src/AIModel/AIBase.cxx b/src/AIModel/AIBase.cxx index 41e2460f1..e878a9b45 100644 --- a/src/AIModel/AIBase.cxx +++ b/src/AIModel/AIBase.cxx @@ -244,6 +244,8 @@ void FGAIBase::readFromScenario(SGPropertyNode* scFileNode) setPath(scFileNode->getStringValue("model", fgGetString("/sim/multiplay/default-model", default_model))); + setFallbackModelIndex(scFileNode->getIntValue("fallback-model-index", 0)); + setHeading(scFileNode->getDoubleValue("heading", 0.0)); setSpeed(scFileNode->getDoubleValue("speed", 0.0)); setAltitude(scFileNode->getDoubleValue("altitude", 0.0)); @@ -446,24 +448,51 @@ std::vector FGAIBase::resolveModelPath(ModelSearchOrder searchOrder std::vector path_list; if (searchOrder == DATA_ONLY) { + SG_LOG(SG_AI, SG_DEBUG, "Resolving model path: DATA only"); auto p = simgear::SGModelLib::findDataFile(model_path); if (!p.empty()) { // We've got a model, use it _installed = true; + SG_LOG(SG_AI, SG_DEBUG, "Found model " << p); path_list.push_back(p); } else { // No model, so fall back to the default path_list.push_back(fgGetString("/sim/multiplay/default-model", default_model)); } } else { + SG_LOG(SG_AI, SG_DEBUG, "Resolving model path: PREFER_AI/PREFER_DATA"); // We're either PREFER_AI or PREFER_DATA. Find an AI model first. for (SGPath p : globals->get_data_paths("AI")) { p.append(model_path); if (p.exists()) { + SG_LOG(SG_AI, SG_DEBUG, "Found AI model: " << p.local8BitStr()); path_list.push_back(p.local8BitStr()); break; } - } // of AI data paths iteration + } + + if (path_list.empty()) { + // Fall back on the fallback-model-index which is a lookup into + // /sim/multiplay/fallback-models/model[] + std::string fallback_path; + const SGPropertyNode* fallbackNode = + globals->get_props()->getNode("/sim/multiplay/fallback-models/model", _getFallbackModelIndex(), false); + + if (fallbackNode != NULL) { + fallback_path = fallbackNode->getStringValue(); + } else { + fallback_path = globals->get_props()->getNode("/sim/multiplay/fallback-models/model", 0, true)->getStringValue(); + } + + for (SGPath p : globals->get_data_paths()) { + p.append(fallback_path); + if (p.exists()) { + SG_LOG(SG_AI, SG_DEBUG, "Found fallback model path for index " << _fallback_model_index << ": " << p.local8BitStr()); + path_list.push_back(p.local8BitStr()); + break; + } + } + } if ((searchOrder == PREFER_AI) && !path_list.empty()) { // if we prefer AI, and we've got a valid AI path from above, then use it, we're done @@ -471,18 +500,13 @@ std::vector FGAIBase::resolveModelPath(ModelSearchOrder searchOrder return path_list; } - // At this point we've either still to find a valid path, or we're - // looking for a regular model to display at closer range. + // At this point we're looking for a regular model to display at closer range. auto p = simgear::SGModelLib::findDataFile(model_path); if (!p.empty()) { _installed = true; + SG_LOG(SG_AI, SG_DEBUG, "Found DATA model " << p); path_list.insert(path_list.begin(), p); } - - if (path_list.empty()) { - // No model found at all, so fall back to the default - path_list.push_back(fgGetString("/sim/multiplay/default-model", default_model)); - } } /* @@ -503,10 +527,10 @@ bool FGAIBase::init(ModelSearchOrder searchOrder) return false; } - vector model_list = resolveModelPath(searchOrder); - props->addChild("type")->setStringValue("AI"); _modeldata = new FGAIModelData(props); + + vector model_list = resolveModelPath(searchOrder); _model= SGModelLib::loadPagedModel(model_list, props, _modeldata); _model->setName("AI-model range animation node"); @@ -982,6 +1006,10 @@ const char* FGAIBase::_getSubmodel() const { return _submodel.c_str(); } +const int FGAIBase::_getFallbackModelIndex() const { + return _fallback_model_index; +} + void FGAIBase::CalculateMach() { // Calculate rho at altitude, using standard atmosphere // For the temperature T and the pressure p, diff --git a/src/AIModel/AIBase.hxx b/src/AIModel/AIBase.hxx index 1392caf80..5f2ae1e25 100644 --- a/src/AIModel/AIBase.hxx +++ b/src/AIModel/AIBase.hxx @@ -74,6 +74,7 @@ public: void updateInterior(); void setManager(FGAIManager* mgr, SGPropertyNode* p); void setPath( const char* model ); + void setFallbackModelIndex(const int i ); void setSMPath( const std::string& p ); void setCallSign(const std::string& ); void setSpeed( double speed_KTAS ); @@ -114,7 +115,7 @@ public: int _getSubID() const; bool getDie(); - bool isValid() const; + bool isValid() const; void setFlightPlan(std::unique_ptr f); @@ -208,7 +209,8 @@ protected: double rotation; // value used by radar display instrument double ht_diff; // value used by radar display instrument - std::string model_path; //Path to the 3D model + std::string model_path; // Path to the 3D model + int _fallback_model_index; // Index into /sim/multiplay/fallback-models[] SGModelPlacement aip; bool delete_me; @@ -316,10 +318,9 @@ public: const char* _getTriggerNode() const; const char* _getName() const; const char* _getSubmodel() const; - + const int _getFallbackModelIndex() const; // These are used in the Mach number calculations - double rho; double T; // temperature, degs farenheit double p; // pressure lbs/sq ft @@ -348,6 +349,10 @@ inline void FGAIBase::setPath(const char* model ) { model_path.append(model); } +inline void FGAIBase::setFallbackModelIndex(const int i ) { + _fallback_model_index = i; +} + inline void FGAIBase::setSMPath(const std::string& p) { _path = p; } diff --git a/src/AIModel/AIManager.cxx b/src/AIModel/AIManager.cxx index ef2400ee2..0be69a448 100644 --- a/src/AIModel/AIManager.cxx +++ b/src/AIModel/AIManager.cxx @@ -134,8 +134,6 @@ FGAIManager::init() { globals->get_commands()->addCommand("unload-scenario", this, &FGAIManager::unloadScenarioCommand); _environmentVisiblity = fgGetNode("/environment/visibility-m"); - _mp_use_detailed_models = fgGetNode("/sim/multiplay/use-detailed-models", true); - // Create an (invisible) AIAircraft representation of the current // users's aircraft, that mimicks the user aircraft's behavior. @@ -315,7 +313,7 @@ FGAIManager::attach(FGAIBase *model) modelPolicy = FGAIBase::PREFER_AI; break; case FGAIBase::otMultiplayer: - modelPolicy = this->_mp_use_detailed_models->getBoolValue() ? FGAIBase::PREFER_DATA : FGAIBase::PREFER_AI; + modelPolicy = FGAIBase::PREFER_DATA; break; default: break; diff --git a/src/AIModel/AIManager.hxx b/src/AIModel/AIManager.hxx index 5e304d7a8..dfa63205e 100644 --- a/src/AIModel/AIManager.hxx +++ b/src/AIModel/AIManager.hxx @@ -120,8 +120,6 @@ private: SGPropertyNode_ptr wind_from_east_node; SGPropertyNode_ptr wind_from_north_node; SGPropertyNode_ptr _environmentVisiblity; - SGPropertyNode_ptr _mp_use_detailed_models; - ai_list_type ai_list; diff --git a/src/AIModel/AIMultiplayer.cxx b/src/AIModel/AIMultiplayer.cxx index d565eb192..9bb4fd2ae 100644 --- a/src/AIModel/AIMultiplayer.cxx +++ b/src/AIModel/AIMultiplayer.cxx @@ -51,7 +51,7 @@ FGAIMultiplayer::FGAIMultiplayer() : playerLag = 0.03; compensateLag = 1; -} +} FGAIMultiplayer::~FGAIMultiplayer() { } @@ -59,6 +59,7 @@ FGAIMultiplayer::~FGAIMultiplayer() { bool FGAIMultiplayer::init(ModelSearchOrder searchOrder) { props->setStringValue("sim/model/path", model_path); + props->setIntValue("sim/model/fallback-model-index", _getFallbackModelIndex()); //refuel_node = fgGetNode("systems/refuel/contact", true); isTanker = false; // do this until this property is // passed over the net @@ -86,7 +87,7 @@ void FGAIMultiplayer::bind() { //2018.1 mp-clock-mode indicates the clock mode that the client is running, so for backwards // compatibility ensure this is initialized to 0 which means pre 2018.1 props->setIntValue("sim/multiplay/mp-clock-mode", 0); - + tie("refuel/contact", SGRawValuePointer(&contact)); tie("tanker", SGRawValuePointer(&isTanker)); @@ -95,7 +96,7 @@ void FGAIMultiplayer::bind() { _uBodyNode = props->getNode("velocities/uBody-fps", true); _vBodyNode = props->getNode("velocities/vBody-fps", true); _wBodyNode = props->getNode("velocities/wBody-fps", true); - + #define AIMPROProp(type, name) \ SGRawValueMethods(*this, &FGAIMultiplayer::get##name) @@ -209,7 +210,7 @@ void FGAIMultiplayer::update(double dt) if (fabs(err) < fabs(systemIncrement)) systemIncrement = err; mTimeOffset += systemIncrement; - + SG_LOG(SG_AI, SG_DEBUG, "Offset adjust system: time offset = " << mTimeOffset << ", expected longitudinal position error due to " " current adjustment of the offset: " @@ -219,7 +220,7 @@ void FGAIMultiplayer::update(double dt) // Compute the time in the feeders time scale which fits the current time - // we need to + // we need to double tInterp = curtime + mTimeOffset; SGVec3d ecPos; @@ -266,14 +267,14 @@ void FGAIMultiplayer::update(double dt) case props::STRING: case props::UNSPECIFIED: pIt->second->setStringValue((*firstPropIt)->string_value); - //cout << "Str: " << (*firstPropIt)->string_value << "\n"; + //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 { @@ -348,7 +349,7 @@ void FGAIMultiplayer::update(double dt) case props::INT: case props::BOOL: case props::LONG: - // Jean Pellotier, 2018-01-02 : we don't want interpolation for integer values, they are mostly used + // Jean Pellotier, 2018-01-02 : we don't want interpolation for integer values, they are mostly used // for non linearly changing values (e.g. transponder etc ...) // fixes: https://sourceforge.net/p/flightgear/codetickets/1885/ pIt->second->setIntValue((*nextPropIt)->int_value); @@ -441,7 +442,7 @@ void FGAIMultiplayer::update(double dt) while (firstPropIt != firstPropItEnd) { PropertyMap::iterator pIt = mPropertyMap.find((*firstPropIt)->id); //cout << " Setting property..." << (*firstPropIt)->id; - + if (pIt != mPropertyMap.end()) { switch ((*firstPropIt)->type) { @@ -466,17 +467,17 @@ void FGAIMultiplayer::update(double dt) pIt->second->setFloatValue((*firstPropIt)->float_value); //cout << "Unk: " << (*firstPropIt)->float_value << "\n"; break; - } + } } else { SG_LOG(SG_AI, SG_DEBUG, "Unable to find property: " << (*firstPropIt)->id << "\n"); } - + ++firstPropIt; } } - + // extract the position pos = SGGeod::fromCart(ecPos); double recent_alt_ft = altitude_ft; diff --git a/src/MultiPlayer/multiplaymgr.cxx b/src/MultiPlayer/multiplaymgr.cxx index 7f963be3b..9bf3cf3d9 100644 --- a/src/MultiPlayer/multiplaymgr.cxx +++ b/src/MultiPlayer/multiplaymgr.cxx @@ -24,7 +24,7 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // $Id$ -// +// ////////////////////////////////////////////////////////////////////// #ifdef HAVE_CONFIG_H @@ -61,7 +61,7 @@ using namespace std; #define MAX_TEXT_SIZE 768 // Increased for 2017.3 to allow for long Emesary messages. /* * With the MP2017(V2) protocol it should be possible to transmit using a different type/encoding than the property has, - * so it should be possible to transmit a bool as + * so it should be possible to transmit a bool as */ enum TransmissionType { TT_ASIS = 0, // transmit as defined in the property. This is the default @@ -71,23 +71,23 @@ enum TransmissionType { TT_STRING = simgear::props::STRING, TT_SHORTINT = 0x100, TT_SHORT_FLOAT_NORM = 0x101, // -1 .. 1 encoded into a short int (16 bit) - TT_SHORT_FLOAT_1 = 0x102, //range -3276.7 .. 3276.7 float encoded into a short int (16 bit) - TT_SHORT_FLOAT_2 = 0x103, //range -327.67 .. 327.67 float encoded into a short int (16 bit) - TT_SHORT_FLOAT_3 = 0x104, //range -32.767 .. 32.767 float encoded into a short int (16 bit) - TT_SHORT_FLOAT_4 = 0x105, //range -3.2767 .. 3.2767 float encoded into a short int (16 bit) + TT_SHORT_FLOAT_1 = 0x102, //range -3276.7 .. 3276.7 float encoded into a short int (16 bit) + TT_SHORT_FLOAT_2 = 0x103, //range -327.67 .. 327.67 float encoded into a short int (16 bit) + TT_SHORT_FLOAT_3 = 0x104, //range -32.767 .. 32.767 float encoded into a short int (16 bit) + TT_SHORT_FLOAT_4 = 0x105, //range -3.2767 .. 3.2767 float encoded into a short int (16 bit) TT_BOOLARRAY, TT_CHAR, TT_NOSEND, // Do not send this property - probably the receive element for a custom encoded property }; /* * Definitions for the version of the protocol to use to transmit the items defined in the IdPropertyList - * + * * The MP2017(V2) protocol allows for much better packing of strings, new types that are transmitted in 4bytes by transmitting * with short int (sometimes scaled) for the values (a lot of the properties that are transmitted will pack nicely into 16bits). * The MP2017(V2) protocol also allows for properties to be transmitted automatically as a different type and the encode/decode will * take this into consideration. * The pad magic is used to force older clients to use verifyProperties and as the first property transmitted is short int encoded it - * will cause the rest of the packet to be discarded. This is the section of the packet that contains the properties defined in the list + * will cause the rest of the packet to be discarded. This is the section of the packet that contains the properties defined in the list * here - the basic motion properties remain compatible, so the older client will see just the model, not chat, not animations etc. * The lower 16 bits of the prop_id (version) are the version and the upper 16bits are for meta data. */ @@ -114,20 +114,22 @@ const int BOOLARRAY_END_ID = BOOLARRAY_BASE_3; // Transmission uses a buffer to build the value for each array block. const int MAX_BOOL_BUFFERS = 3; -// starting with 2018.1 we will now append new properties for each version at the end of the list - as this provides much +// starting with 2018.1 we will now append new properties for each version at the end of the list - as this provides much // better backwards compatibility. const int V2018_1_BASE = 11990; const int EMESARYBRIDGETYPE_BASE = 12200; const int EMESARYBRIDGE_BASE = 12000; +const int V2018_3_BASE = 13000; +const int FALLBACK_MODEL_ID = 13000; /* * definition of properties that are to be transmitted. - * New for 2017.2: + * New for 2017.2: * 1. TransmitAs - this causes the property to be transmitted on the wire using the * specified format transparently. * 2. version - the minimum version of the protocol that is required to transmit a property. * Does not apply to incoming properties - as these will be decoded correctly when received - * 3. encode_for_transmit - method that will convert from and to the packet for the value + * 3. encode_for_transmit - method that will convert from and to the packet for the value * Allows specific conversion rules to be applied; such as conversion of a string to an integer for transmission. * 4. decode_received - decodes received data * - when using the encode/decode methods there should be both specified, however if the result of the encode @@ -145,7 +147,7 @@ struct IdPropertyList { xdr_data_t* (*encode_for_transmit)(const IdPropertyList *propDef, const xdr_data_t*, FGPropertyData*); xdr_data_t* (*decode_received)(const IdPropertyList *propDef, const xdr_data_t*, FGPropertyData*); }; - + static const IdPropertyList* findProperty(unsigned id); /* @@ -220,7 +222,7 @@ static const IdPropertyList sIdPropertyList[] = { { 105, "surface-positions/speedbrake-pos-norm", simgear::props::FLOAT, TT_SHORT_FLOAT_NORM, V1_1_PROP_ID, NULL, NULL }, { 106, "gear/tailhook/position-norm", simgear::props::FLOAT, TT_SHORT_FLOAT_NORM, V1_1_PROP_ID, NULL, NULL }, { 107, "gear/launchbar/position-norm", simgear::props::FLOAT, TT_SHORT_FLOAT_NORM, V1_1_PROP_ID, NULL, NULL }, - // + // { 108, "gear/launchbar/state", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, encode_launchbar_state_for_transmission, NULL }, { 109, "gear/launchbar/holdback-position-norm", simgear::props::FLOAT, TT_SHORT_FLOAT_NORM, V1_1_PROP_ID, NULL, NULL }, { 110, "canopy/position-norm", simgear::props::FLOAT, TT_SHORT_FLOAT_NORM, V1_1_PROP_ID, NULL, NULL }, @@ -538,7 +540,7 @@ static const IdPropertyList sIdPropertyList[] = { { BOOLARRAY_BASE_2 + 8, "sim/multiplay/generic/bool[39]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL }, { BOOLARRAY_BASE_2 + 9, "sim/multiplay/generic/bool[40]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL }, { BOOLARRAY_BASE_2 + 10, "sim/multiplay/generic/bool[41]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL }, - // out of sequence between the block and the buffer becuase of a typo. repurpose the first as that way [72] will work + // out of sequence between the block and the buffer becuase of a typo. repurpose the first as that way [72] will work // correctly on older versions. { BOOLARRAY_BASE_2 + 11, "sim/multiplay/generic/bool[91]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL }, { BOOLARRAY_BASE_2 + 12, "sim/multiplay/generic/bool[42]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL }, @@ -572,7 +574,7 @@ static const IdPropertyList sIdPropertyList[] = { { BOOLARRAY_BASE_3 + 8, "sim/multiplay/generic/bool[69]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL }, { BOOLARRAY_BASE_3 + 9, "sim/multiplay/generic/bool[70]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL }, { BOOLARRAY_BASE_3 + 10, "sim/multiplay/generic/bool[71]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL }, - // out of sequence between the block and the buffer becuase of a typo. repurpose the first as that way [72] will work + // out of sequence between the block and the buffer becuase of a typo. repurpose the first as that way [72] will work // correctly on older versions. { BOOLARRAY_BASE_3 + 11, "sim/multiplay/generic/bool[92]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL }, { BOOLARRAY_BASE_3 + 12, "sim/multiplay/generic/bool[72]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL }, @@ -661,12 +663,14 @@ static const IdPropertyList sIdPropertyList[] = { { EMESARYBRIDGETYPE_BASE + 27, "sim/multiplay/emesary/bridge-type[27]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL }, { EMESARYBRIDGETYPE_BASE + 28, "sim/multiplay/emesary/bridge-type[28]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL }, { EMESARYBRIDGETYPE_BASE + 29, "sim/multiplay/emesary/bridge-type[29]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL }, + + { FALLBACK_MODEL_ID, "sim/model/fallback-model-index", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL }, }; /* * For the 2017.x version 2 protocol the properties are sent in two partitions, * the first of these is a V1 protocol packet (which should be fine with all clients), and a V2 partition * which will contain the newly supported shortint and fixed string encoding schemes. - * This is to possibly allow for easier V1/V2 conversion - as the packet can simply be truncated at the + * This is to possibly allow for easier V1/V2 conversion - as the packet can simply be truncated at the * first V2 property based on ID. */ const int MAX_PARTITIONS = 2; @@ -692,7 +696,7 @@ namespace { return id < rhs.id; } - }; + }; } const IdPropertyList* findProperty(unsigned id) @@ -716,7 +720,7 @@ namespace while (xdr < end) { unsigned id = XDR_decode_uint32(*xdr); const IdPropertyList* plist = findProperty(id); - + if (plist) { xdr++; // How we decode the remainder of the property depends on the type @@ -764,7 +768,7 @@ namespace // cerr << "Unknown Prop type " << id << " " << type << "\n"; xdr++; break; - } + } } else { // give up; this is a malformed property list. @@ -938,7 +942,7 @@ do_multiplayer_refreshserverlist (const SGPropertyNode * arg, SGPropertyNode * r // MultiplayMgr constructor // ////////////////////////////////////////////////////////////////////// -FGMultiplayMgr::FGMultiplayMgr() +FGMultiplayMgr::FGMultiplayMgr() { mInitialised = false; mHaveServer = false; @@ -960,7 +964,7 @@ FGMultiplayMgr::FGMultiplayMgr() // MultiplayMgr destructor // ////////////////////////////////////////////////////////////////////// -FGMultiplayMgr::~FGMultiplayMgr() +FGMultiplayMgr::~FGMultiplayMgr() { globals->get_commands()->removeCommand("multiplayer-connect"); globals->get_commands()->removeCommand("multiplayer-disconnect"); @@ -974,7 +978,7 @@ FGMultiplayMgr::~FGMultiplayMgr() // ////////////////////////////////////////////////////////////////////// void -FGMultiplayMgr::init (void) +FGMultiplayMgr::init (void) { ////////////////////////////////////////////////// // Initialise object if not already done @@ -995,18 +999,18 @@ FGMultiplayMgr::init (void) string rxAddress = fgGetString("/sim/multiplay/rxhost"); short txPort = fgGetInt("/sim/multiplay/txport", 5000); string txAddress = fgGetString("/sim/multiplay/txhost"); - + int hz = fgGetInt("/sim/multiplay/tx-rate-hz", 10); if (hz < 1) { hz = 10; } - + mDt = 1.0 / hz; mTimeUntilSend = 0.0; - + mCallsign = fgGetString("/sim/multiplay/callsign"); fgGetNode("/sim/multiplay/callsign", true)->setAttribute(SGPropertyNode::PRESERVE, true); - + if ((!txAddress.empty()) && (txAddress!="0")) { mServer.set(txAddress.c_str(), txPort); if (strncmp (mServer.getHost(), "0.0.0.0", 8) == 0) { @@ -1038,7 +1042,7 @@ FGMultiplayMgr::init (void) SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-rxaddress="<get_props()->addChangeListener(mListener, false); @@ -1077,28 +1081,28 @@ FGMultiplayMgr::init (void) // ////////////////////////////////////////////////////////////////////// void -FGMultiplayMgr::shutdown (void) +FGMultiplayMgr::shutdown (void) { fgSetBool("/sim/multiplay/online", false); - + if (mSocket.get()) { mSocket->close(); - mSocket.reset(); + mSocket.reset(); } - + MultiPlayerMap::iterator it = mMultiPlayerMap.begin(), end = mMultiPlayerMap.end(); for (; it != end; ++it) { it->second->setDie(true); } mMultiPlayerMap.clear(); - + if (mListener) { globals->get_props()->removeChangeListener(mListener); delete mListener; mListener = NULL; } - + mInitialised = false; } // FGMultiplayMgr::Close(void) ////////////////////////////////////////////////////////////////////// @@ -1182,7 +1186,7 @@ union FGMultiplayMgr::MsgBuf { return reinterpret_cast(Msg + Header.MsgLen); } - + xdr_data2_t double_val; char Msg[MAX_PACKET_SIZE]; T_MsgHdr Header; @@ -1237,7 +1241,7 @@ FGMultiplayMgr::SendMyPosition(const FGExternalMotionData& motionInfo) /* * This is to provide a level of compatibility with the new V2 packets. * By setting padding it will force older clients to use verify properties which will - * bail out if there are any unknown props + * bail out if there are any unknown props * MP2017(V2) (for V1 clients) will always have an unknown property because V2 transmits * the protocol version as the very first property as a shortint. */ @@ -1260,7 +1264,7 @@ FGMultiplayMgr::SendMyPosition(const FGExternalMotionData& motionInfo) PosMsg->linearAccel[i] = XDR_encode_float (0.0); PosMsg->angularAccel[i] = XDR_encode_float (0.0); } - // all other data remains unchanged (resend last state) + // all other data remains unchanged (resend last state) } else { @@ -1501,12 +1505,12 @@ FGMultiplayMgr::SendMyPosition(const FGExternalMotionData& motionInfo) // String is complicated. It consists of // The length of the string // The string itself - // Padding to the nearest 4-bytes. + // Padding to the nearest 4-bytes. const char* lcharptr = (*it)->string_value; if (lcharptr != 0) { - // Add the length + // Add the length ////cout << "String length: " << strlen(lcharptr) << "\n"; uint32_t len = strlen(lcharptr); if (len >= MAX_TEXT_SIZE) @@ -1663,10 +1667,10 @@ FGMultiplayMgr::SendTextMessage(const string &MsgText) ////////////////////////////////////////////////// unsigned iNextBlockPosition = 0; T_ChatMsg ChatMsg; - + char Msg[sizeof(T_MsgHdr) + sizeof(T_ChatMsg)]; while (iNextBlockPosition < MsgText.length()) { - strncpy (ChatMsg.Text, + 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'; @@ -1676,8 +1680,8 @@ FGMultiplayMgr::SendTextMessage(const string &MsgText) iNextBlockPosition += MAX_CHAT_MSG_LEN - 1; } - - + + } // FGMultiplayMgr::SendTextMessage () ////////////////////////////////////////////////////////////////////// @@ -1686,10 +1690,10 @@ FGMultiplayMgr::SendTextMessage(const string &MsgText) // 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(double dt) +FGMultiplayMgr::update(double dt) { if (!mInitialised) return; @@ -1712,7 +1716,7 @@ FGMultiplayMgr::update(double dt) do { MsgBuf msgBuf; ////////////////////////////////////////////////// - // Although the recv call asks for + // 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. @@ -1949,7 +1953,7 @@ FGMultiplayMgr::Send() break; } default: - // FIXME Currently default to a float. + // FIXME Currently default to a float. //cout << "Unknown type when iterating through props: " << pData->type << "\n"; pData->float_value = it->second->getFloatValue(); break; @@ -1977,6 +1981,7 @@ FGMultiplayMgr::ProcessPosMsg(const FGMultiplayMgr::MsgBuf& Msg, } const T_PositionMsg* PosMsg = Msg.posMsg(); FGExternalMotionData motionInfo; + int fallback_model_index = 0; motionInfo.time = XDR_decode_double(PosMsg->time); motionInfo.lag = XDR_decode_double(PosMsg->lag); for (unsigned i = 0; i < 3; ++i) @@ -2023,7 +2028,7 @@ FGMultiplayMgr::ProcessPosMsg(const FGMultiplayMgr::MsgBuf& Msg, // strict about the validity of the property values. const xdr_data_t* xdr = Msg.properties(); const xdr_data_t* data = xdr; - + /* * with V2 we use the pad to forcefully invoke older clients to verify (and discard) * our new protocol. @@ -2066,7 +2071,7 @@ FGMultiplayMgr::ProcessPosMsg(const FGMultiplayMgr::MsgBuf& Msg, // Check the ID actually exists and get the type const IdPropertyList* plist = findProperty(id); - + if (plist) { FGPropertyData* pData = new FGPropertyData; @@ -2184,7 +2189,7 @@ FGMultiplayMgr::ProcessPosMsg(const FGMultiplayMgr::MsgBuf& Msg, // String is complicated. It consists of // The length of the string // The string itself - // Padding to the nearest 4-bytes. + // Padding to the nearest 4-bytes. uint32_t length = XDR_decode_uint32(*xdr); xdr++; //cout << length << " "; @@ -2222,23 +2227,30 @@ FGMultiplayMgr::ProcessPosMsg(const FGMultiplayMgr::MsgBuf& Msg, } if (pData) motionInfo.properties.push_back(pData); + + // Special case - we need the /sim/model/fallback-model-index to create + // the MP model + if (pData->id == FALLBACK_MODEL_ID) { + fallback_model_index = pData->int_value; + SG_LOG(SG_NETWORK, SG_DEBUG, "Found Fallback model index in message " << fallback_model_index); + } } else { // We failed to find the property. We'll try the next packet immediately. SG_LOG(SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::ProcessPosMsg - " "message from " << MsgHdr->Callsign << " has unknown property id " - << id); + << id); - // At this point the packet must be considered to be unreadable + // At this point the packet must be considered to be unreadable // as we have no way of knowing the length of this property (it could be a string) - break; + break; } } noprops: FGAIMultiplayer* mp = getMultiplayer(MsgHdr->Callsign); if (!mp) - mp = addMultiplayer(MsgHdr->Callsign, PosMsg->Model); + mp = addMultiplayer(MsgHdr->Callsign, PosMsg->Model, fallback_model_index); mp->addMotionInfo(motionInfo, stamp); } // FGMultiplayMgr::ProcessPosMsg() ////////////////////////////////////////////////////////////////////// @@ -2259,14 +2271,14 @@ FGMultiplayMgr::ProcessChatMsg(const MsgBuf& Msg, << "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); @@ -2301,13 +2313,15 @@ FGMultiplayMgr::FillMsgHdr(T_MsgHdr *MsgHdr, int MsgId, unsigned _len) FGAIMultiplayer* FGMultiplayMgr::addMultiplayer(const std::string& callsign, - const std::string& modelName) + const std::string& modelName, + const int fallback_model_index) { if (0 < mMultiPlayerMap.count(callsign)) return mMultiPlayerMap[callsign].get(); FGAIMultiplayer* mp = new FGAIMultiplayer; mp->setPath(modelName.c_str()); + mp->setFallbackModelIndex(fallback_model_index); mp->setCallSign(callsign); mMultiPlayerMap[callsign] = mp; @@ -2338,21 +2352,21 @@ FGMultiplayMgr::findProperties() if (!mPropertiesChanged) { return; } - + mPropertiesChanged = false; - + for (unsigned i = 0; i < numProperties; ++i) { const char* name = sIdPropertyList[i].name; SGPropertyNode* pNode = globals->get_props()->getNode(name); if (!pNode) { continue; } - + int id = sIdPropertyList[i].id; if (mPropertyMap.find(id) != mPropertyMap.end()) { continue; // already activated } - + mPropertyMap[id] = pNode; mPropertyDefinition[id] = &sIdPropertyList[i]; SG_LOG(SG_NETWORK, SG_DEBUG, "activating MP property:" << pNode->getPath()); diff --git a/src/MultiPlayer/multiplaymgr.hxx b/src/MultiPlayer/multiplaymgr.hxx index 953fb70f6..5d6f0ef42 100644 --- a/src/MultiPlayer/multiplaymgr.hxx +++ b/src/MultiPlayer/multiplaymgr.hxx @@ -23,7 +23,7 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // $Id$ -// +// ////////////////////////////////////////////////////////////////////// #ifndef MULTIPLAYMGR_H @@ -50,24 +50,24 @@ class FGAIMultiplayer; class FGMultiplayMgr : public SGSubsystem { -public: +public: FGMultiplayMgr(); ~FGMultiplayMgr(); - + virtual void init(void); virtual void update(double dt); - + virtual void shutdown(void); virtual void reinit(); - + // transmitter - + void SendTextMessage(const std::string &sMsgText); // receiver - + private: friend class MPPropertyListener; - + void setPropertiesChanged() { mPropertiesChanged = true; @@ -82,14 +82,15 @@ private: } void findProperties(); - + void Send(); void SendMyPosition(const FGExternalMotionData& motionInfo); short get_scaled_short(double v, double scale); union MsgBuf; FGAIMultiplayer* addMultiplayer(const std::string& callsign, - const std::string& modelName); + const std::string& modelName, + const int fallback_model_index); FGAIMultiplayer* getMultiplayer(const std::string& callsign); void FillMsgHdr(T_MsgHdr *MsgHdr, int iMsgId, unsigned _len = 0u); void ProcessPosMsg(const MsgBuf& Msg, const simgear::IPAddress& SenderAddress, @@ -106,7 +107,7 @@ private: bool mHaveServer; bool mInitialised; std::string mCallsign; - + // Map between the property id's from the multiplayers network packets // and the property nodes typedef std::map > PropertyMap; @@ -123,10 +124,9 @@ private: bool mPropertiesChanged; MPPropertyListener* mListener; - + double mDt; // reciprocal of /sim/multiplay/tx-rate-hz double mTimeUntilSend; }; #endif -