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",
|
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,
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue