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:
parent
23196184cc
commit
e43fe82094
7 changed files with 149 additions and 105 deletions
|
@ -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<std::string> FGAIBase::resolveModelPath(ModelSearchOrder searchOrder
|
|||
std::vector<std::string> 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<std::string> 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<string> model_list = resolveModelPath(searchOrder);
|
||||
|
||||
props->addChild("type")->setStringValue("AI");
|
||||
_modeldata = new FGAIModelData(props);
|
||||
|
||||
vector<string> 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,
|
||||
|
|
|
@ -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<FGAIFlightPlan> 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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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<bool>(&contact));
|
||||
tie("tanker", SGRawValuePointer<bool>(&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<FGAIMultiplayer, type>(*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;
|
||||
|
|
|
@ -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="<<rxAddress );
|
||||
SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-rxport= "<<rxPort);
|
||||
SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-callsign= "<<mCallsign);
|
||||
|
||||
|
||||
mSocket.reset(new simgear::Socket());
|
||||
if (!mSocket->open(false)) {
|
||||
SG_LOG( SG_NETWORK, SG_ALERT,
|
||||
|
@ -1052,7 +1056,7 @@ FGMultiplayMgr::init (void)
|
|||
<< strerror(errno) << "(errno " << errno << ")");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
mPropertiesChanged = true;
|
||||
mListener = new MPPropertyListener(this);
|
||||
globals->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<const xdr_data_t*>(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<const T_ChatMsg *>(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());
|
||||
|
|
|
@ -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<unsigned int, SGSharedPtr<SGPropertyNode> > PropertyMap;
|
||||
|
@ -123,10 +124,9 @@ private:
|
|||
bool mPropertiesChanged;
|
||||
|
||||
MPPropertyListener* mListener;
|
||||
|
||||
|
||||
double mDt; // reciprocal of /sim/multiplay/tx-rate-hz
|
||||
double mTimeUntilSend;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
|
Loading…
Reference in a new issue