1
0
Fork 0

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.
This commit is contained in:
Stuart Buchanan 2018-07-27 19:51:37 +01:00
parent 23196184cc
commit e43fe82094
7 changed files with 149 additions and 105 deletions

View file

@ -244,6 +244,8 @@ void FGAIBase::readFromScenario(SGPropertyNode* scFileNode)
setPath(scFileNode->getStringValue("model", setPath(scFileNode->getStringValue("model",
fgGetString("/sim/multiplay/default-model", default_model))); fgGetString("/sim/multiplay/default-model", default_model)));
setFallbackModelIndex(scFileNode->getIntValue("fallback-model-index", 0));
setHeading(scFileNode->getDoubleValue("heading", 0.0)); setHeading(scFileNode->getDoubleValue("heading", 0.0));
setSpeed(scFileNode->getDoubleValue("speed", 0.0)); setSpeed(scFileNode->getDoubleValue("speed", 0.0));
setAltitude(scFileNode->getDoubleValue("altitude", 0.0)); setAltitude(scFileNode->getDoubleValue("altitude", 0.0));
@ -446,24 +448,51 @@ std::vector<std::string> FGAIBase::resolveModelPath(ModelSearchOrder searchOrder
std::vector<std::string> path_list; std::vector<std::string> path_list;
if (searchOrder == DATA_ONLY) { if (searchOrder == DATA_ONLY) {
SG_LOG(SG_AI, SG_DEBUG, "Resolving model path: DATA only");
auto p = simgear::SGModelLib::findDataFile(model_path); auto p = simgear::SGModelLib::findDataFile(model_path);
if (!p.empty()) { if (!p.empty()) {
// We've got a model, use it // We've got a model, use it
_installed = true; _installed = true;
SG_LOG(SG_AI, SG_DEBUG, "Found model " << p);
path_list.push_back(p); path_list.push_back(p);
} else { } else {
// No model, so fall back to the default // No model, so fall back to the default
path_list.push_back(fgGetString("/sim/multiplay/default-model", default_model)); path_list.push_back(fgGetString("/sim/multiplay/default-model", default_model));
} }
} else { } 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. // We're either PREFER_AI or PREFER_DATA. Find an AI model first.
for (SGPath p : globals->get_data_paths("AI")) { for (SGPath p : globals->get_data_paths("AI")) {
p.append(model_path); p.append(model_path);
if (p.exists()) { if (p.exists()) {
SG_LOG(SG_AI, SG_DEBUG, "Found AI model: " << p.local8BitStr());
path_list.push_back(p.local8BitStr()); path_list.push_back(p.local8BitStr());
break; 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 ((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 // 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<std::string> FGAIBase::resolveModelPath(ModelSearchOrder searchOrder
return path_list; return path_list;
} }
// At this point we've either still to find a valid path, or we're // At this point we're looking for a regular model to display at closer range.
// looking for a regular model to display at closer range.
auto p = simgear::SGModelLib::findDataFile(model_path); auto p = simgear::SGModelLib::findDataFile(model_path);
if (!p.empty()) { if (!p.empty()) {
_installed = true; _installed = true;
SG_LOG(SG_AI, SG_DEBUG, "Found DATA model " << p);
path_list.insert(path_list.begin(), 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; return false;
} }
vector<string> model_list = resolveModelPath(searchOrder);
props->addChild("type")->setStringValue("AI"); props->addChild("type")->setStringValue("AI");
_modeldata = new FGAIModelData(props); _modeldata = new FGAIModelData(props);
vector<string> model_list = resolveModelPath(searchOrder);
_model= SGModelLib::loadPagedModel(model_list, props, _modeldata); _model= SGModelLib::loadPagedModel(model_list, props, _modeldata);
_model->setName("AI-model range animation node"); _model->setName("AI-model range animation node");
@ -982,6 +1006,10 @@ const char* FGAIBase::_getSubmodel() const {
return _submodel.c_str(); return _submodel.c_str();
} }
const int FGAIBase::_getFallbackModelIndex() const {
return _fallback_model_index;
}
void FGAIBase::CalculateMach() { void FGAIBase::CalculateMach() {
// Calculate rho at altitude, using standard atmosphere // Calculate rho at altitude, using standard atmosphere
// For the temperature T and the pressure p, // For the temperature T and the pressure p,

View file

@ -74,6 +74,7 @@ public:
void updateInterior(); void updateInterior();
void setManager(FGAIManager* mgr, SGPropertyNode* p); void setManager(FGAIManager* mgr, SGPropertyNode* p);
void setPath( const char* model ); void setPath( const char* model );
void setFallbackModelIndex(const int i );
void setSMPath( const std::string& p ); void setSMPath( const std::string& p );
void setCallSign(const std::string& ); void setCallSign(const std::string& );
void setSpeed( double speed_KTAS ); void setSpeed( double speed_KTAS );
@ -114,7 +115,7 @@ public:
int _getSubID() const; int _getSubID() const;
bool getDie(); bool getDie();
bool isValid() const; bool isValid() const;
void setFlightPlan(std::unique_ptr<FGAIFlightPlan> f); void setFlightPlan(std::unique_ptr<FGAIFlightPlan> f);
@ -208,7 +209,8 @@ protected:
double rotation; // value used by radar display instrument double rotation; // value used by radar display instrument
double ht_diff; // 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; SGModelPlacement aip;
bool delete_me; bool delete_me;
@ -316,10 +318,9 @@ public:
const char* _getTriggerNode() const; const char* _getTriggerNode() const;
const char* _getName() const; const char* _getName() const;
const char* _getSubmodel() const; const char* _getSubmodel() const;
const int _getFallbackModelIndex() const;
// These are used in the Mach number calculations // These are used in the Mach number calculations
double rho; double rho;
double T; // temperature, degs farenheit double T; // temperature, degs farenheit
double p; // pressure lbs/sq ft double p; // pressure lbs/sq ft
@ -348,6 +349,10 @@ inline void FGAIBase::setPath(const char* model ) {
model_path.append(model); model_path.append(model);
} }
inline void FGAIBase::setFallbackModelIndex(const int i ) {
_fallback_model_index = i;
}
inline void FGAIBase::setSMPath(const std::string& p) { inline void FGAIBase::setSMPath(const std::string& p) {
_path = p; _path = p;
} }

View file

@ -134,8 +134,6 @@ FGAIManager::init() {
globals->get_commands()->addCommand("unload-scenario", this, &FGAIManager::unloadScenarioCommand); globals->get_commands()->addCommand("unload-scenario", this, &FGAIManager::unloadScenarioCommand);
_environmentVisiblity = fgGetNode("/environment/visibility-m"); _environmentVisiblity = fgGetNode("/environment/visibility-m");
_mp_use_detailed_models = fgGetNode("/sim/multiplay/use-detailed-models", true);
// Create an (invisible) AIAircraft representation of the current // Create an (invisible) AIAircraft representation of the current
// users's aircraft, that mimicks the user aircraft's behavior. // users's aircraft, that mimicks the user aircraft's behavior.
@ -315,7 +313,7 @@ FGAIManager::attach(FGAIBase *model)
modelPolicy = FGAIBase::PREFER_AI; modelPolicy = FGAIBase::PREFER_AI;
break; break;
case FGAIBase::otMultiplayer: case FGAIBase::otMultiplayer:
modelPolicy = this->_mp_use_detailed_models->getBoolValue() ? FGAIBase::PREFER_DATA : FGAIBase::PREFER_AI; modelPolicy = FGAIBase::PREFER_DATA;
break; break;
default: default:
break; break;

View file

@ -120,8 +120,6 @@ private:
SGPropertyNode_ptr wind_from_east_node; SGPropertyNode_ptr wind_from_east_node;
SGPropertyNode_ptr wind_from_north_node; SGPropertyNode_ptr wind_from_north_node;
SGPropertyNode_ptr _environmentVisiblity; SGPropertyNode_ptr _environmentVisiblity;
SGPropertyNode_ptr _mp_use_detailed_models;
ai_list_type ai_list; ai_list_type ai_list;

View file

@ -51,7 +51,7 @@ FGAIMultiplayer::FGAIMultiplayer() :
playerLag = 0.03; playerLag = 0.03;
compensateLag = 1; compensateLag = 1;
} }
FGAIMultiplayer::~FGAIMultiplayer() { FGAIMultiplayer::~FGAIMultiplayer() {
} }
@ -59,6 +59,7 @@ FGAIMultiplayer::~FGAIMultiplayer() {
bool FGAIMultiplayer::init(ModelSearchOrder searchOrder) bool FGAIMultiplayer::init(ModelSearchOrder searchOrder)
{ {
props->setStringValue("sim/model/path", model_path); props->setStringValue("sim/model/path", model_path);
props->setIntValue("sim/model/fallback-model-index", _getFallbackModelIndex());
//refuel_node = fgGetNode("systems/refuel/contact", true); //refuel_node = fgGetNode("systems/refuel/contact", true);
isTanker = false; // do this until this property is isTanker = false; // do this until this property is
// passed over the net // 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 //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 // compatibility ensure this is initialized to 0 which means pre 2018.1
props->setIntValue("sim/multiplay/mp-clock-mode", 0); props->setIntValue("sim/multiplay/mp-clock-mode", 0);
tie("refuel/contact", SGRawValuePointer<bool>(&contact)); tie("refuel/contact", SGRawValuePointer<bool>(&contact));
tie("tanker", SGRawValuePointer<bool>(&isTanker)); tie("tanker", SGRawValuePointer<bool>(&isTanker));
@ -95,7 +96,7 @@ void FGAIMultiplayer::bind() {
_uBodyNode = props->getNode("velocities/uBody-fps", true); _uBodyNode = props->getNode("velocities/uBody-fps", true);
_vBodyNode = props->getNode("velocities/vBody-fps", true); _vBodyNode = props->getNode("velocities/vBody-fps", true);
_wBodyNode = props->getNode("velocities/wBody-fps", true); _wBodyNode = props->getNode("velocities/wBody-fps", true);
#define AIMPROProp(type, name) \ #define AIMPROProp(type, name) \
SGRawValueMethods<FGAIMultiplayer, type>(*this, &FGAIMultiplayer::get##name) SGRawValueMethods<FGAIMultiplayer, type>(*this, &FGAIMultiplayer::get##name)
@ -209,7 +210,7 @@ void FGAIMultiplayer::update(double dt)
if (fabs(err) < fabs(systemIncrement)) if (fabs(err) < fabs(systemIncrement))
systemIncrement = err; systemIncrement = err;
mTimeOffset += systemIncrement; mTimeOffset += systemIncrement;
SG_LOG(SG_AI, SG_DEBUG, "Offset adjust system: time offset = " SG_LOG(SG_AI, SG_DEBUG, "Offset adjust system: time offset = "
<< mTimeOffset << ", expected longitudinal position error due to " << mTimeOffset << ", expected longitudinal position error due to "
" current adjustment of the offset: " " 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 // Compute the time in the feeders time scale which fits the current time
// we need to // we need to
double tInterp = curtime + mTimeOffset; double tInterp = curtime + mTimeOffset;
SGVec3d ecPos; SGVec3d ecPos;
@ -266,14 +267,14 @@ void FGAIMultiplayer::update(double dt)
case props::STRING: case props::STRING:
case props::UNSPECIFIED: case props::UNSPECIFIED:
pIt->second->setStringValue((*firstPropIt)->string_value); pIt->second->setStringValue((*firstPropIt)->string_value);
//cout << "Str: " << (*firstPropIt)->string_value << "\n"; //cout << "Str: " << (*firstPropIt)->string_value << "\n";
break; break;
default: default:
// FIXME - currently defaults to float values // FIXME - currently defaults to float values
pIt->second->setFloatValue((*firstPropIt)->float_value); pIt->second->setFloatValue((*firstPropIt)->float_value);
//cout << "Unknown: " << (*firstPropIt)->float_value << "\n"; //cout << "Unknown: " << (*firstPropIt)->float_value << "\n";
break; break;
} }
} }
else else
{ {
@ -348,7 +349,7 @@ void FGAIMultiplayer::update(double dt)
case props::INT: case props::INT:
case props::BOOL: case props::BOOL:
case props::LONG: 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 ...) // for non linearly changing values (e.g. transponder etc ...)
// fixes: https://sourceforge.net/p/flightgear/codetickets/1885/ // fixes: https://sourceforge.net/p/flightgear/codetickets/1885/
pIt->second->setIntValue((*nextPropIt)->int_value); pIt->second->setIntValue((*nextPropIt)->int_value);
@ -441,7 +442,7 @@ void FGAIMultiplayer::update(double dt)
while (firstPropIt != firstPropItEnd) { while (firstPropIt != firstPropItEnd) {
PropertyMap::iterator pIt = mPropertyMap.find((*firstPropIt)->id); PropertyMap::iterator pIt = mPropertyMap.find((*firstPropIt)->id);
//cout << " Setting property..." << (*firstPropIt)->id; //cout << " Setting property..." << (*firstPropIt)->id;
if (pIt != mPropertyMap.end()) if (pIt != mPropertyMap.end())
{ {
switch ((*firstPropIt)->type) { switch ((*firstPropIt)->type) {
@ -466,17 +467,17 @@ void FGAIMultiplayer::update(double dt)
pIt->second->setFloatValue((*firstPropIt)->float_value); pIt->second->setFloatValue((*firstPropIt)->float_value);
//cout << "Unk: " << (*firstPropIt)->float_value << "\n"; //cout << "Unk: " << (*firstPropIt)->float_value << "\n";
break; break;
} }
} }
else else
{ {
SG_LOG(SG_AI, SG_DEBUG, "Unable to find property: " << (*firstPropIt)->id << "\n"); SG_LOG(SG_AI, SG_DEBUG, "Unable to find property: " << (*firstPropIt)->id << "\n");
} }
++firstPropIt; ++firstPropIt;
} }
} }
// extract the position // extract the position
pos = SGGeod::fromCart(ecPos); pos = SGGeod::fromCart(ecPos);
double recent_alt_ft = altitude_ft; double recent_alt_ft = altitude_ft;

View file

@ -24,7 +24,7 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
// //
// $Id$ // $Id$
// //
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
#ifdef HAVE_CONFIG_H #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. #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, * 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 { enum TransmissionType {
TT_ASIS = 0, // transmit as defined in the property. This is the default 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_STRING = simgear::props::STRING,
TT_SHORTINT = 0x100, TT_SHORTINT = 0x100,
TT_SHORT_FLOAT_NORM = 0x101, // -1 .. 1 encoded into a short int (16 bit) 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_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_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_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_4 = 0x105, //range -3.2767 .. 3.2767 float encoded into a short int (16 bit)
TT_BOOLARRAY, TT_BOOLARRAY,
TT_CHAR, TT_CHAR,
TT_NOSEND, // Do not send this property - probably the receive element for a custom encoded property 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 * 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 * 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). * 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 * 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. * 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 * 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. * 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. * 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. // Transmission uses a buffer to build the value for each array block.
const int MAX_BOOL_BUFFERS = 3; 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. // better backwards compatibility.
const int V2018_1_BASE = 11990; const int V2018_1_BASE = 11990;
const int EMESARYBRIDGETYPE_BASE = 12200; const int EMESARYBRIDGETYPE_BASE = 12200;
const int EMESARYBRIDGE_BASE = 12000; 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. * 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 * 1. TransmitAs - this causes the property to be transmitted on the wire using the
* specified format transparently. * specified format transparently.
* 2. version - the minimum version of the protocol that is required to transmit a property. * 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 * 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. * Allows specific conversion rules to be applied; such as conversion of a string to an integer for transmission.
* 4. decode_received - decodes received data * 4. decode_received - decodes received data
* - when using the encode/decode methods there should be both specified, however if the result of the encode * - 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* (*encode_for_transmit)(const IdPropertyList *propDef, const xdr_data_t*, FGPropertyData*);
xdr_data_t* (*decode_received)(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); 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 }, { 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 }, { 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 }, { 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 }, { 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 }, { 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 }, { 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 + 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 + 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 }, { 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. // 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 + 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 }, { 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 + 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 + 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 }, { 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. // 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 + 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 }, { 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 + 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 + 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 }, { 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, * 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 * 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. * 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. * first V2 property based on ID.
*/ */
const int MAX_PARTITIONS = 2; const int MAX_PARTITIONS = 2;
@ -692,7 +696,7 @@ namespace
{ {
return id < rhs.id; return id < rhs.id;
} }
}; };
} }
const IdPropertyList* findProperty(unsigned id) const IdPropertyList* findProperty(unsigned id)
@ -716,7 +720,7 @@ namespace
while (xdr < end) { while (xdr < end) {
unsigned id = XDR_decode_uint32(*xdr); unsigned id = XDR_decode_uint32(*xdr);
const IdPropertyList* plist = findProperty(id); const IdPropertyList* plist = findProperty(id);
if (plist) { if (plist) {
xdr++; xdr++;
// How we decode the remainder of the property depends on the type // How we decode the remainder of the property depends on the type
@ -764,7 +768,7 @@ namespace
// cerr << "Unknown Prop type " << id << " " << type << "\n"; // cerr << "Unknown Prop type " << id << " " << type << "\n";
xdr++; xdr++;
break; break;
} }
} }
else { else {
// give up; this is a malformed property list. // give up; this is a malformed property list.
@ -938,7 +942,7 @@ do_multiplayer_refreshserverlist (const SGPropertyNode * arg, SGPropertyNode * r
// MultiplayMgr constructor // MultiplayMgr constructor
// //
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
FGMultiplayMgr::FGMultiplayMgr() FGMultiplayMgr::FGMultiplayMgr()
{ {
mInitialised = false; mInitialised = false;
mHaveServer = false; mHaveServer = false;
@ -960,7 +964,7 @@ FGMultiplayMgr::FGMultiplayMgr()
// MultiplayMgr destructor // MultiplayMgr destructor
// //
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
FGMultiplayMgr::~FGMultiplayMgr() FGMultiplayMgr::~FGMultiplayMgr()
{ {
globals->get_commands()->removeCommand("multiplayer-connect"); globals->get_commands()->removeCommand("multiplayer-connect");
globals->get_commands()->removeCommand("multiplayer-disconnect"); globals->get_commands()->removeCommand("multiplayer-disconnect");
@ -974,7 +978,7 @@ FGMultiplayMgr::~FGMultiplayMgr()
// //
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
void void
FGMultiplayMgr::init (void) FGMultiplayMgr::init (void)
{ {
////////////////////////////////////////////////// //////////////////////////////////////////////////
// Initialise object if not already done // Initialise object if not already done
@ -995,18 +999,18 @@ FGMultiplayMgr::init (void)
string rxAddress = fgGetString("/sim/multiplay/rxhost"); string rxAddress = fgGetString("/sim/multiplay/rxhost");
short txPort = fgGetInt("/sim/multiplay/txport", 5000); short txPort = fgGetInt("/sim/multiplay/txport", 5000);
string txAddress = fgGetString("/sim/multiplay/txhost"); string txAddress = fgGetString("/sim/multiplay/txhost");
int hz = fgGetInt("/sim/multiplay/tx-rate-hz", 10); int hz = fgGetInt("/sim/multiplay/tx-rate-hz", 10);
if (hz < 1) { if (hz < 1) {
hz = 10; hz = 10;
} }
mDt = 1.0 / hz; mDt = 1.0 / hz;
mTimeUntilSend = 0.0; mTimeUntilSend = 0.0;
mCallsign = fgGetString("/sim/multiplay/callsign"); mCallsign = fgGetString("/sim/multiplay/callsign");
fgGetNode("/sim/multiplay/callsign", true)->setAttribute(SGPropertyNode::PRESERVE, true); fgGetNode("/sim/multiplay/callsign", true)->setAttribute(SGPropertyNode::PRESERVE, true);
if ((!txAddress.empty()) && (txAddress!="0")) { if ((!txAddress.empty()) && (txAddress!="0")) {
mServer.set(txAddress.c_str(), txPort); mServer.set(txAddress.c_str(), txPort);
if (strncmp (mServer.getHost(), "0.0.0.0", 8) == 0) { 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="<<rxAddress ); SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-rxaddress="<<rxAddress );
SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-rxport= "<<rxPort); SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-rxport= "<<rxPort);
SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-callsign= "<<mCallsign); SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-callsign= "<<mCallsign);
mSocket.reset(new simgear::Socket()); mSocket.reset(new simgear::Socket());
if (!mSocket->open(false)) { if (!mSocket->open(false)) {
SG_LOG( SG_NETWORK, SG_ALERT, SG_LOG( SG_NETWORK, SG_ALERT,
@ -1052,7 +1056,7 @@ FGMultiplayMgr::init (void)
<< strerror(errno) << "(errno " << errno << ")"); << strerror(errno) << "(errno " << errno << ")");
return; return;
} }
mPropertiesChanged = true; mPropertiesChanged = true;
mListener = new MPPropertyListener(this); mListener = new MPPropertyListener(this);
globals->get_props()->addChangeListener(mListener, false); globals->get_props()->addChangeListener(mListener, false);
@ -1077,28 +1081,28 @@ FGMultiplayMgr::init (void)
// //
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
void void
FGMultiplayMgr::shutdown (void) FGMultiplayMgr::shutdown (void)
{ {
fgSetBool("/sim/multiplay/online", false); fgSetBool("/sim/multiplay/online", false);
if (mSocket.get()) { if (mSocket.get()) {
mSocket->close(); mSocket->close();
mSocket.reset(); mSocket.reset();
} }
MultiPlayerMap::iterator it = mMultiPlayerMap.begin(), MultiPlayerMap::iterator it = mMultiPlayerMap.begin(),
end = mMultiPlayerMap.end(); end = mMultiPlayerMap.end();
for (; it != end; ++it) { for (; it != end; ++it) {
it->second->setDie(true); it->second->setDie(true);
} }
mMultiPlayerMap.clear(); mMultiPlayerMap.clear();
if (mListener) { if (mListener) {
globals->get_props()->removeChangeListener(mListener); globals->get_props()->removeChangeListener(mListener);
delete mListener; delete mListener;
mListener = NULL; mListener = NULL;
} }
mInitialised = false; mInitialised = false;
} // FGMultiplayMgr::Close(void) } // FGMultiplayMgr::Close(void)
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@ -1182,7 +1186,7 @@ union FGMultiplayMgr::MsgBuf
{ {
return reinterpret_cast<const xdr_data_t*>(Msg + Header.MsgLen); return reinterpret_cast<const xdr_data_t*>(Msg + Header.MsgLen);
} }
xdr_data2_t double_val; xdr_data2_t double_val;
char Msg[MAX_PACKET_SIZE]; char Msg[MAX_PACKET_SIZE];
T_MsgHdr Header; 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. * 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 * 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 * 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. * 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->linearAccel[i] = XDR_encode_float (0.0);
PosMsg->angularAccel[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 else
{ {
@ -1501,12 +1505,12 @@ FGMultiplayMgr::SendMyPosition(const FGExternalMotionData& motionInfo)
// String is complicated. It consists of // String is complicated. It consists of
// The length of the string // The length of the string
// The string itself // The string itself
// Padding to the nearest 4-bytes. // Padding to the nearest 4-bytes.
const char* lcharptr = (*it)->string_value; const char* lcharptr = (*it)->string_value;
if (lcharptr != 0) if (lcharptr != 0)
{ {
// Add the length // Add the length
////cout << "String length: " << strlen(lcharptr) << "\n"; ////cout << "String length: " << strlen(lcharptr) << "\n";
uint32_t len = strlen(lcharptr); uint32_t len = strlen(lcharptr);
if (len >= MAX_TEXT_SIZE) if (len >= MAX_TEXT_SIZE)
@ -1663,10 +1667,10 @@ FGMultiplayMgr::SendTextMessage(const string &MsgText)
////////////////////////////////////////////////// //////////////////////////////////////////////////
unsigned iNextBlockPosition = 0; unsigned iNextBlockPosition = 0;
T_ChatMsg ChatMsg; T_ChatMsg ChatMsg;
char Msg[sizeof(T_MsgHdr) + sizeof(T_ChatMsg)]; char Msg[sizeof(T_MsgHdr) + sizeof(T_ChatMsg)];
while (iNextBlockPosition < MsgText.length()) { while (iNextBlockPosition < MsgText.length()) {
strncpy (ChatMsg.Text, strncpy (ChatMsg.Text,
MsgText.substr(iNextBlockPosition, MAX_CHAT_MSG_LEN - 1).c_str(), MsgText.substr(iNextBlockPosition, MAX_CHAT_MSG_LEN - 1).c_str(),
MAX_CHAT_MSG_LEN); MAX_CHAT_MSG_LEN);
ChatMsg.Text[MAX_CHAT_MSG_LEN - 1] = '\0'; ChatMsg.Text[MAX_CHAT_MSG_LEN - 1] = '\0';
@ -1676,8 +1680,8 @@ FGMultiplayMgr::SendTextMessage(const string &MsgText)
iNextBlockPosition += MAX_CHAT_MSG_LEN - 1; iNextBlockPosition += MAX_CHAT_MSG_LEN - 1;
} }
} // FGMultiplayMgr::SendTextMessage () } // FGMultiplayMgr::SendTextMessage ()
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@ -1686,10 +1690,10 @@ FGMultiplayMgr::SendTextMessage(const string &MsgText)
// Name: ProcessData // Name: ProcessData
// Description: Processes data waiting at the receive socket. The // Description: Processes data waiting at the receive socket. The
// processing ends when there is no more data at the socket. // processing ends when there is no more data at the socket.
// //
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
void void
FGMultiplayMgr::update(double dt) FGMultiplayMgr::update(double dt)
{ {
if (!mInitialised) if (!mInitialised)
return; return;
@ -1712,7 +1716,7 @@ FGMultiplayMgr::update(double dt)
do { do {
MsgBuf msgBuf; MsgBuf msgBuf;
////////////////////////////////////////////////// //////////////////////////////////////////////////
// Although the recv call asks for // Although the recv call asks for
// MAX_PACKET_SIZE of data, the number of bytes // MAX_PACKET_SIZE of data, the number of bytes
// returned will only be that of the next // returned will only be that of the next
// packet waiting to be processed. // packet waiting to be processed.
@ -1949,7 +1953,7 @@ FGMultiplayMgr::Send()
break; break;
} }
default: default:
// FIXME Currently default to a float. // FIXME Currently default to a float.
//cout << "Unknown type when iterating through props: " << pData->type << "\n"; //cout << "Unknown type when iterating through props: " << pData->type << "\n";
pData->float_value = it->second->getFloatValue(); pData->float_value = it->second->getFloatValue();
break; break;
@ -1977,6 +1981,7 @@ FGMultiplayMgr::ProcessPosMsg(const FGMultiplayMgr::MsgBuf& Msg,
} }
const T_PositionMsg* PosMsg = Msg.posMsg(); const T_PositionMsg* PosMsg = Msg.posMsg();
FGExternalMotionData motionInfo; FGExternalMotionData motionInfo;
int fallback_model_index = 0;
motionInfo.time = XDR_decode_double(PosMsg->time); motionInfo.time = XDR_decode_double(PosMsg->time);
motionInfo.lag = XDR_decode_double(PosMsg->lag); motionInfo.lag = XDR_decode_double(PosMsg->lag);
for (unsigned i = 0; i < 3; ++i) 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. // strict about the validity of the property values.
const xdr_data_t* xdr = Msg.properties(); const xdr_data_t* xdr = Msg.properties();
const xdr_data_t* data = xdr; const xdr_data_t* data = xdr;
/* /*
* with V2 we use the pad to forcefully invoke older clients to verify (and discard) * with V2 we use the pad to forcefully invoke older clients to verify (and discard)
* our new protocol. * our new protocol.
@ -2066,7 +2071,7 @@ FGMultiplayMgr::ProcessPosMsg(const FGMultiplayMgr::MsgBuf& Msg,
// Check the ID actually exists and get the type // Check the ID actually exists and get the type
const IdPropertyList* plist = findProperty(id); const IdPropertyList* plist = findProperty(id);
if (plist) if (plist)
{ {
FGPropertyData* pData = new FGPropertyData; FGPropertyData* pData = new FGPropertyData;
@ -2184,7 +2189,7 @@ FGMultiplayMgr::ProcessPosMsg(const FGMultiplayMgr::MsgBuf& Msg,
// String is complicated. It consists of // String is complicated. It consists of
// The length of the string // The length of the string
// The string itself // The string itself
// Padding to the nearest 4-bytes. // Padding to the nearest 4-bytes.
uint32_t length = XDR_decode_uint32(*xdr); uint32_t length = XDR_decode_uint32(*xdr);
xdr++; xdr++;
//cout << length << " "; //cout << length << " ";
@ -2222,23 +2227,30 @@ FGMultiplayMgr::ProcessPosMsg(const FGMultiplayMgr::MsgBuf& Msg,
} }
if (pData) if (pData)
motionInfo.properties.push_back(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 else
{ {
// We failed to find the property. We'll try the next packet immediately. // We failed to find the property. We'll try the next packet immediately.
SG_LOG(SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::ProcessPosMsg - " SG_LOG(SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::ProcessPosMsg - "
"message from " << MsgHdr->Callsign << " has unknown property id " "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) // as we have no way of knowing the length of this property (it could be a string)
break; break;
} }
} }
noprops: noprops:
FGAIMultiplayer* mp = getMultiplayer(MsgHdr->Callsign); FGAIMultiplayer* mp = getMultiplayer(MsgHdr->Callsign);
if (!mp) if (!mp)
mp = addMultiplayer(MsgHdr->Callsign, PosMsg->Model); mp = addMultiplayer(MsgHdr->Callsign, PosMsg->Model, fallback_model_index);
mp->addMotionInfo(motionInfo, stamp); mp->addMotionInfo(motionInfo, stamp);
} // FGMultiplayMgr::ProcessPosMsg() } // FGMultiplayMgr::ProcessPosMsg()
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@ -2259,14 +2271,14 @@ FGMultiplayMgr::ProcessChatMsg(const MsgBuf& Msg,
<< "Chat message received with insufficient data" ); << "Chat message received with insufficient data" );
return; return;
} }
char *chatStr = new char[MsgHdr->MsgLen - sizeof(T_MsgHdr)]; char *chatStr = new char[MsgHdr->MsgLen - sizeof(T_MsgHdr)];
const T_ChatMsg* ChatMsg const T_ChatMsg* ChatMsg
= reinterpret_cast<const T_ChatMsg *>(Msg.Msg + sizeof(T_MsgHdr)); = reinterpret_cast<const T_ChatMsg *>(Msg.Msg + sizeof(T_MsgHdr));
strncpy(chatStr, ChatMsg->Text, strncpy(chatStr, ChatMsg->Text,
MsgHdr->MsgLen - sizeof(T_MsgHdr)); MsgHdr->MsgLen - sizeof(T_MsgHdr));
chatStr[MsgHdr->MsgLen - sizeof(T_MsgHdr) - 1] = '\0'; chatStr[MsgHdr->MsgLen - sizeof(T_MsgHdr) - 1] = '\0';
SG_LOG (SG_NETWORK, SG_WARN, "Chat [" << MsgHdr->Callsign << "]" SG_LOG (SG_NETWORK, SG_WARN, "Chat [" << MsgHdr->Callsign << "]"
<< " " << chatStr); << " " << chatStr);
@ -2301,13 +2313,15 @@ FGMultiplayMgr::FillMsgHdr(T_MsgHdr *MsgHdr, int MsgId, unsigned _len)
FGAIMultiplayer* FGAIMultiplayer*
FGMultiplayMgr::addMultiplayer(const std::string& callsign, FGMultiplayMgr::addMultiplayer(const std::string& callsign,
const std::string& modelName) const std::string& modelName,
const int fallback_model_index)
{ {
if (0 < mMultiPlayerMap.count(callsign)) if (0 < mMultiPlayerMap.count(callsign))
return mMultiPlayerMap[callsign].get(); return mMultiPlayerMap[callsign].get();
FGAIMultiplayer* mp = new FGAIMultiplayer; FGAIMultiplayer* mp = new FGAIMultiplayer;
mp->setPath(modelName.c_str()); mp->setPath(modelName.c_str());
mp->setFallbackModelIndex(fallback_model_index);
mp->setCallSign(callsign); mp->setCallSign(callsign);
mMultiPlayerMap[callsign] = mp; mMultiPlayerMap[callsign] = mp;
@ -2338,21 +2352,21 @@ FGMultiplayMgr::findProperties()
if (!mPropertiesChanged) { if (!mPropertiesChanged) {
return; return;
} }
mPropertiesChanged = false; mPropertiesChanged = false;
for (unsigned i = 0; i < numProperties; ++i) { for (unsigned i = 0; i < numProperties; ++i) {
const char* name = sIdPropertyList[i].name; const char* name = sIdPropertyList[i].name;
SGPropertyNode* pNode = globals->get_props()->getNode(name); SGPropertyNode* pNode = globals->get_props()->getNode(name);
if (!pNode) { if (!pNode) {
continue; continue;
} }
int id = sIdPropertyList[i].id; int id = sIdPropertyList[i].id;
if (mPropertyMap.find(id) != mPropertyMap.end()) { if (mPropertyMap.find(id) != mPropertyMap.end()) {
continue; // already activated continue; // already activated
} }
mPropertyMap[id] = pNode; mPropertyMap[id] = pNode;
mPropertyDefinition[id] = &sIdPropertyList[i]; mPropertyDefinition[id] = &sIdPropertyList[i];
SG_LOG(SG_NETWORK, SG_DEBUG, "activating MP property:" << pNode->getPath()); SG_LOG(SG_NETWORK, SG_DEBUG, "activating MP property:" << pNode->getPath());

View file

@ -23,7 +23,7 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
// //
// $Id$ // $Id$
// //
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
#ifndef MULTIPLAYMGR_H #ifndef MULTIPLAYMGR_H
@ -50,24 +50,24 @@ class FGAIMultiplayer;
class FGMultiplayMgr : public SGSubsystem class FGMultiplayMgr : public SGSubsystem
{ {
public: public:
FGMultiplayMgr(); FGMultiplayMgr();
~FGMultiplayMgr(); ~FGMultiplayMgr();
virtual void init(void); virtual void init(void);
virtual void update(double dt); virtual void update(double dt);
virtual void shutdown(void); virtual void shutdown(void);
virtual void reinit(); virtual void reinit();
// transmitter // transmitter
void SendTextMessage(const std::string &sMsgText); void SendTextMessage(const std::string &sMsgText);
// receiver // receiver
private: private:
friend class MPPropertyListener; friend class MPPropertyListener;
void setPropertiesChanged() void setPropertiesChanged()
{ {
mPropertiesChanged = true; mPropertiesChanged = true;
@ -82,14 +82,15 @@ private:
} }
void findProperties(); void findProperties();
void Send(); void Send();
void SendMyPosition(const FGExternalMotionData& motionInfo); void SendMyPosition(const FGExternalMotionData& motionInfo);
short get_scaled_short(double v, double scale); short get_scaled_short(double v, double scale);
union MsgBuf; union MsgBuf;
FGAIMultiplayer* addMultiplayer(const std::string& callsign, 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); FGAIMultiplayer* getMultiplayer(const std::string& callsign);
void FillMsgHdr(T_MsgHdr *MsgHdr, int iMsgId, unsigned _len = 0u); void FillMsgHdr(T_MsgHdr *MsgHdr, int iMsgId, unsigned _len = 0u);
void ProcessPosMsg(const MsgBuf& Msg, const simgear::IPAddress& SenderAddress, void ProcessPosMsg(const MsgBuf& Msg, const simgear::IPAddress& SenderAddress,
@ -106,7 +107,7 @@ private:
bool mHaveServer; bool mHaveServer;
bool mInitialised; bool mInitialised;
std::string mCallsign; std::string mCallsign;
// Map between the property id's from the multiplayers network packets // Map between the property id's from the multiplayers network packets
// and the property nodes // and the property nodes
typedef std::map<unsigned int, SGSharedPtr<SGPropertyNode> > PropertyMap; typedef std::map<unsigned int, SGSharedPtr<SGPropertyNode> > PropertyMap;
@ -123,10 +124,9 @@ private:
bool mPropertiesChanged; bool mPropertiesChanged;
MPPropertyListener* mListener; MPPropertyListener* mListener;
double mDt; // reciprocal of /sim/multiplay/tx-rate-hz double mDt; // reciprocal of /sim/multiplay/tx-rate-hz
double mTimeUntilSend; double mTimeUntilSend;
}; };
#endif #endif