Merge branch 'next' of ssh://git.code.sf.net/p/flightgear/flightgear into next
This commit is contained in:
commit
b0d707c3f4
15 changed files with 569 additions and 120 deletions
|
@ -233,7 +233,7 @@ else(EVENT_INPUT)
|
||||||
endif(EVENT_INPUT)
|
endif(EVENT_INPUT)
|
||||||
|
|
||||||
# check required dependencies
|
# check required dependencies
|
||||||
find_package(Boost REQUIRED)
|
find_package(Boost REQUIRED)
|
||||||
find_package(OpenGL REQUIRED)
|
find_package(OpenGL REQUIRED)
|
||||||
find_package(OpenSceneGraph 3.2.0 REQUIRED
|
find_package(OpenSceneGraph 3.2.0 REQUIRED
|
||||||
osgText
|
osgText
|
||||||
|
|
|
@ -24,10 +24,8 @@ if (MSVC AND MSVC_3RDPARTY_ROOT)
|
||||||
set( OSG_MSVC ${OSG_MSVC}140 )
|
set( OSG_MSVC ${OSG_MSVC}140 )
|
||||||
elseif (${MSVC_VERSION} EQUAL 1800)
|
elseif (${MSVC_VERSION} EQUAL 1800)
|
||||||
set( OSG_MSVC ${OSG_MSVC}120 )
|
set( OSG_MSVC ${OSG_MSVC}120 )
|
||||||
elseif (${MSVC_VERSION} EQUAL 1700)
|
else ()
|
||||||
set( OSG_MSVC ${OSG_MSVC}110 )
|
message(FATAL_ERROR "Visual Studio 2013/2015 is required now")
|
||||||
elseif (${MSVC_VERSION} EQUAL 1600)
|
|
||||||
set( OSG_MSVC ${OSG_MSVC}100 )
|
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (CMAKE_CL_64)
|
if (CMAKE_CL_64)
|
||||||
|
@ -39,24 +37,18 @@ if (MSVC AND MSVC_3RDPARTY_ROOT)
|
||||||
set( BOOST_LIB lib )
|
set( BOOST_LIB lib )
|
||||||
endif (CMAKE_CL_64)
|
endif (CMAKE_CL_64)
|
||||||
|
|
||||||
GET_FILENAME_COMPONENT(MSVC_ROOT_PARENT_DIR ${MSVC_3RDPARTY_ROOT} PATH)
|
set (CMAKE_LIBRARY_PATH ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenScenegraph/lib ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenRTI/lib ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/SimGear/lib )
|
||||||
set (CMAKE_LIBRARY_PATH ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenScenegraph/lib ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenRTI/lib ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/SimGear/lib $(BOOST_ROOT)/$(BOOST_LIB) )
|
|
||||||
set (CMAKE_INCLUDE_PATH ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenScenegraph/include ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenRTI/include ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/SimGear/include)
|
set (CMAKE_INCLUDE_PATH ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenScenegraph/include ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenRTI/include ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/SimGear/include)
|
||||||
find_path(BOOST_ROOT boost/version.hpp
|
|
||||||
${MSVC_ROOT_PARENT_DIR}
|
if(NOT BOOST_INCLUDEDIR)
|
||||||
${MSVC_3RDPARTY_ROOT}/boost
|
# if this variable was not set by the user, set it to 3rdparty root's
|
||||||
${MSVC_3RDPARTY_ROOT}/boost_1_52_0
|
# parent dir, which is the normal location for people using our
|
||||||
${MSVC_3RDPARTY_ROOT}/boost_1_51_0
|
# windows-3rd-party repo
|
||||||
${MSVC_3RDPARTY_ROOT}/boost_1_50_0
|
GET_FILENAME_COMPONENT(MSVC_ROOT_PARENT_DIR ${MSVC_3RDPARTY_ROOT} PATH)
|
||||||
${MSVC_3RDPARTY_ROOT}/boost_1_49_0
|
set(BOOST_INCLUDEDIR ${MSVC_ROOT_PARENT_DIR})
|
||||||
${MSVC_3RDPARTY_ROOT}/boost_1_48_0
|
message(STATUS "BOOST_INCLUDEDIR is ${BOOST_INCLUDEDIR}")
|
||||||
${MSVC_3RDPARTY_ROOT}/boost_1_47_0
|
endif()
|
||||||
${MSVC_3RDPARTY_ROOT}/boost_1_46_1
|
|
||||||
${MSVC_3RDPARTY_ROOT}/boost_1_46_0
|
|
||||||
${MSVC_3RDPARTY_ROOT}/boost_1_45_0
|
|
||||||
${MSVC_3RDPARTY_ROOT}/boost_1_44_0
|
|
||||||
)
|
|
||||||
message(STATUS "BOOST_ROOT is ${BOOST_ROOT}")
|
|
||||||
if (USE_AEONWAVE)
|
if (USE_AEONWAVE)
|
||||||
find_package(AAX COMPONENTS aax REQUIRED)
|
find_package(AAX COMPONENTS aax REQUIRED)
|
||||||
else()
|
else()
|
||||||
|
|
|
@ -127,7 +127,9 @@ void FGFDM::init()
|
||||||
_turb_magnitude_norm = fgGetNode("/environment/turbulence/magnitude-norm", true);
|
_turb_magnitude_norm = fgGetNode("/environment/turbulence/magnitude-norm", true);
|
||||||
_turb_rate_hz = fgGetNode("/environment/turbulence/rate-hz", true);
|
_turb_rate_hz = fgGetNode("/environment/turbulence/rate-hz", true);
|
||||||
_gross_weight_lbs = fgGetNode("/yasim/gross-weight-lbs", true);
|
_gross_weight_lbs = fgGetNode("/yasim/gross-weight-lbs", true);
|
||||||
|
_cg_x = fgGetNode("/yasim/cg-x-m", true);
|
||||||
|
_cg_y = fgGetNode("/yasim/cg-y-m", true);
|
||||||
|
_cg_z = fgGetNode("/yasim/cg-z-m", true);
|
||||||
// Allows the user to start with something other than full fuel
|
// Allows the user to start with something other than full fuel
|
||||||
_airplane.setFuelFraction(fgGetFloat("/sim/fuel-fraction", 1));
|
_airplane.setFuelFraction(fgGetFloat("/sim/fuel-fraction", 1));
|
||||||
|
|
||||||
|
@ -610,6 +612,12 @@ void FGFDM::setOutputProperties(float dt)
|
||||||
float grossWgt = _airplane.getModel()->getBody()->getTotalMass() * KG2LBS;
|
float grossWgt = _airplane.getModel()->getBody()->getTotalMass() * KG2LBS;
|
||||||
_gross_weight_lbs->setFloatValue(grossWgt);
|
_gross_weight_lbs->setFloatValue(grossWgt);
|
||||||
|
|
||||||
|
float cg[3];
|
||||||
|
_airplane.getModel()->getBody()->getCG(cg);
|
||||||
|
_cg_x->setFloatValue(cg[0]);
|
||||||
|
_cg_y->setFloatValue(cg[1]);
|
||||||
|
_cg_z->setFloatValue(cg[2]);
|
||||||
|
|
||||||
ControlMap* cm = _airplane.getControlMap();
|
ControlMap* cm = _airplane.getControlMap();
|
||||||
for(int i=0; i<_controlProps.size(); i++) {
|
for(int i=0; i<_controlProps.size(); i++) {
|
||||||
PropOut* p = (PropOut*)_controlProps.get(i);
|
PropOut* p = (PropOut*)_controlProps.get(i);
|
||||||
|
|
|
@ -104,6 +104,9 @@ private:
|
||||||
|
|
||||||
SGPropertyNode_ptr _turb_magnitude_norm, _turb_rate_hz;
|
SGPropertyNode_ptr _turb_magnitude_norm, _turb_rate_hz;
|
||||||
SGPropertyNode_ptr _gross_weight_lbs;
|
SGPropertyNode_ptr _gross_weight_lbs;
|
||||||
|
SGPropertyNode_ptr _cg_x;
|
||||||
|
SGPropertyNode_ptr _cg_y;
|
||||||
|
SGPropertyNode_ptr _cg_z;
|
||||||
std::vector<SGPropertyNode_ptr> _tank_level_lbs;
|
std::vector<SGPropertyNode_ptr> _tank_level_lbs;
|
||||||
std::vector<ThrusterProps> _thrust_props;
|
std::vector<ThrusterProps> _thrust_props;
|
||||||
std::vector<FuelProps> _fuel_props;
|
std::vector<FuelProps> _fuel_props;
|
||||||
|
|
|
@ -144,9 +144,9 @@ void AircraftItem::toDataStream(QDataStream& ds) const
|
||||||
for (int i=0; i<4; ++i) ds << ratings[i];
|
for (int i=0; i<4; ++i) ds << ratings[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
QPixmap AircraftItem::thumbnail() const
|
QPixmap AircraftItem::thumbnail(bool loadIfRequired) const
|
||||||
{
|
{
|
||||||
if (m_thumbnail.isNull()) {
|
if (m_thumbnail.isNull() && loadIfRequired) {
|
||||||
QFileInfo info(path);
|
QFileInfo info(path);
|
||||||
QDir dir = info.dir();
|
QDir dir = info.dir();
|
||||||
if (dir.exists("thumbnail.jpg")) {
|
if (dir.exists("thumbnail.jpg")) {
|
||||||
|
@ -346,7 +346,7 @@ protected:
|
||||||
virtual void catalogRefreshed(CatalogRef aCatalog, StatusCode aReason)
|
virtual void catalogRefreshed(CatalogRef aCatalog, StatusCode aReason)
|
||||||
{
|
{
|
||||||
if (aReason == STATUS_IN_PROGRESS) {
|
if (aReason == STATUS_IN_PROGRESS) {
|
||||||
qDebug() << "doing refresh of" << QString::fromStdString(aCatalog->url());
|
// nothing to do
|
||||||
} else if ((aReason == STATUS_REFRESHED) || (aReason == STATUS_SUCCESS)) {
|
} else if ((aReason == STATUS_REFRESHED) || (aReason == STATUS_SUCCESS)) {
|
||||||
m_model->refreshPackages();
|
m_model->refreshPackages();
|
||||||
} else {
|
} else {
|
||||||
|
@ -402,19 +402,15 @@ protected:
|
||||||
if (pix.height() > STANDARD_THUMBNAIL_HEIGHT) {
|
if (pix.height() > STANDARD_THUMBNAIL_HEIGHT) {
|
||||||
pix = pix.scaledToHeight(STANDARD_THUMBNAIL_HEIGHT);
|
pix = pix.scaledToHeight(STANDARD_THUMBNAIL_HEIGHT);
|
||||||
}
|
}
|
||||||
m_model->m_thumbnailPixmapCache.insert(QString::fromStdString(aThumbnailUrl),
|
m_model->m_thumbnailPixmapCache.insert(QString::fromStdString(aThumbnailUrl), pix);
|
||||||
pix);
|
|
||||||
|
|
||||||
// notify any affected items. Linear scan here avoids another map/dict
|
// notify any affected items. Linear scan here avoids another map/dict
|
||||||
// structure.
|
// structure.
|
||||||
PackageList::const_iterator it;
|
for (auto pkg : m_model->m_packages) {
|
||||||
int i = 0;
|
const string_list& urls(pkg->thumbnailUrls());
|
||||||
|
auto cit = std::find(urls.begin(), urls.end(), aThumbnailUrl);
|
||||||
for (it=m_model->m_packages.begin(); it != m_model->m_packages.end(); ++it, ++i) {
|
|
||||||
const string_list& urls((*it)->thumbnailUrls());
|
|
||||||
string_list::const_iterator cit = std::find(urls.begin(), urls.end(), aThumbnailUrl);
|
|
||||||
if (cit != urls.end()) {
|
if (cit != urls.end()) {
|
||||||
QModelIndex mi(m_model->index(i + m_model->m_items.size()));
|
QModelIndex mi = indexForPackage(pkg);
|
||||||
m_model->dataChanged(mi, mi);
|
m_model->dataChanged(mi, mi);
|
||||||
}
|
}
|
||||||
} // of packages iteration
|
} // of packages iteration
|
||||||
|
@ -423,9 +419,8 @@ protected:
|
||||||
private:
|
private:
|
||||||
QModelIndex indexForPackage(const PackageRef& ref) const
|
QModelIndex indexForPackage(const PackageRef& ref) const
|
||||||
{
|
{
|
||||||
PackageList::const_iterator it = std::find(m_model->m_packages.begin(),
|
auto it = std::find(m_model->m_packages.begin(),
|
||||||
m_model->m_packages.end(),
|
m_model->m_packages.end(), ref);
|
||||||
ref);
|
|
||||||
if (it == m_model->m_packages.end()) {
|
if (it == m_model->m_packages.end()) {
|
||||||
return QModelIndex();
|
return QModelIndex();
|
||||||
}
|
}
|
||||||
|
@ -480,12 +475,12 @@ void AircraftItemModel::setMessageWidgetVisible(bool vis)
|
||||||
if (vis) {
|
if (vis) {
|
||||||
beginInsertRows(QModelIndex(), 0, 0);
|
beginInsertRows(QModelIndex(), 0, 0);
|
||||||
m_items.prepend(AircraftItemPtr(new AircraftItem));
|
m_items.prepend(AircraftItemPtr(new AircraftItem));
|
||||||
m_activeVariant.prepend(0);
|
m_delegateStates.prepend(DelegateState());
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
} else {
|
} else {
|
||||||
beginRemoveRows(QModelIndex(), 0, 0);
|
beginRemoveRows(QModelIndex(), 0, 0);
|
||||||
m_items.removeAt(0);
|
m_items.removeAt(0);
|
||||||
m_activeVariant.removeAt(0);
|
m_delegateStates.removeAt(0);
|
||||||
endRemoveRows();
|
endRemoveRows();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -510,7 +505,7 @@ void AircraftItemModel::scanDirs()
|
||||||
|
|
||||||
beginRemoveRows(QModelIndex(), firstRow, lastRow);
|
beginRemoveRows(QModelIndex(), firstRow, lastRow);
|
||||||
m_items.remove(firstRow, numToRemove);
|
m_items.remove(firstRow, numToRemove);
|
||||||
m_activeVariant.remove(firstRow, numToRemove);
|
m_delegateStates.remove(firstRow, numToRemove);
|
||||||
endRemoveRows();
|
endRemoveRows();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -545,8 +540,9 @@ void AircraftItemModel::abandonCurrentScan()
|
||||||
void AircraftItemModel::refreshPackages()
|
void AircraftItemModel::refreshPackages()
|
||||||
{
|
{
|
||||||
simgear::pkg::PackageList newPkgs = m_packageRoot->allPackages();
|
simgear::pkg::PackageList newPkgs = m_packageRoot->allPackages();
|
||||||
int firstRow = m_items.size();
|
const int firstRow = m_items.size();
|
||||||
int newSize = newPkgs.size();
|
const int newSize = newPkgs.size();
|
||||||
|
const int newTotalSize = firstRow + newSize;
|
||||||
|
|
||||||
if (m_packages.size() != newPkgs.size()) {
|
if (m_packages.size() != newPkgs.size()) {
|
||||||
int oldSize = m_packages.size();
|
int oldSize = m_packages.size();
|
||||||
|
@ -556,7 +552,7 @@ void AircraftItemModel::refreshPackages()
|
||||||
int lastNewRow = firstRow + newSize - 1;
|
int lastNewRow = firstRow + newSize - 1;
|
||||||
beginInsertRows(QModelIndex(), firstNewRow, lastNewRow);
|
beginInsertRows(QModelIndex(), firstNewRow, lastNewRow);
|
||||||
m_packages = newPkgs;
|
m_packages = newPkgs;
|
||||||
m_packageVariant.resize(newSize);
|
m_delegateStates.resize(newTotalSize);
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
} else {
|
} else {
|
||||||
// shrinking
|
// shrinking
|
||||||
|
@ -564,7 +560,7 @@ void AircraftItemModel::refreshPackages()
|
||||||
int lastOldRow = firstRow + oldSize - 1;
|
int lastOldRow = firstRow + oldSize - 1;
|
||||||
beginRemoveRows(QModelIndex(), firstOldRow, lastOldRow);
|
beginRemoveRows(QModelIndex(), firstOldRow, lastOldRow);
|
||||||
m_packages = newPkgs;
|
m_packages = newPkgs;
|
||||||
m_packageVariant.resize(newSize);
|
m_delegateStates.resize(newTotalSize);
|
||||||
endRemoveRows();
|
endRemoveRows();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -593,13 +589,16 @@ QVariant AircraftItemModel::data(const QModelIndex& index, int role) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (role == AircraftVariantRole) {
|
||||||
|
return m_delegateStates.at(row).variant;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role == AircraftCurrentThumbnailRole) {
|
||||||
|
return m_delegateStates.at(row).thumbnail;
|
||||||
|
}
|
||||||
|
|
||||||
if (row >= m_items.size()) {
|
if (row >= m_items.size()) {
|
||||||
quint32 packageIndex = row - m_items.size();
|
quint32 packageIndex = row - m_items.size();
|
||||||
|
|
||||||
if (role == AircraftVariantRole) {
|
|
||||||
return m_packageVariant.at(packageIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
const PackageRef& pkg(m_packages[packageIndex]);
|
const PackageRef& pkg(m_packages[packageIndex]);
|
||||||
InstallRef ex = pkg->existingInstall();
|
InstallRef ex = pkg->existingInstall();
|
||||||
|
|
||||||
|
@ -609,20 +608,14 @@ QVariant AircraftItemModel::data(const QModelIndex& index, int role) const
|
||||||
return static_cast<quint64>(ex.valid() ? ex->downloadedBytes() : 0);
|
return static_cast<quint64>(ex.valid() ? ex->downloadedBytes() : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
quint32 variantIndex = m_packageVariant.at(packageIndex);
|
return dataFromPackage(pkg, m_delegateStates.at(row), role);
|
||||||
return dataFromPackage(pkg, variantIndex, role);
|
|
||||||
} else {
|
} else {
|
||||||
if (role == AircraftVariantRole) {
|
|
||||||
return m_activeVariant.at(row);
|
|
||||||
}
|
|
||||||
|
|
||||||
quint32 variantIndex = m_activeVariant.at(row);
|
|
||||||
const AircraftItemPtr item(m_items.at(row));
|
const AircraftItemPtr item(m_items.at(row));
|
||||||
return dataFromItem(item, variantIndex, role);
|
return dataFromItem(item, m_delegateStates.at(row), role);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant AircraftItemModel::dataFromItem(AircraftItemPtr item, quint32 variantIndex, int role) const
|
QVariant AircraftItemModel::dataFromItem(AircraftItemPtr item, const DelegateState& state, int role) const
|
||||||
{
|
{
|
||||||
if (role == AircraftVariantCountRole) {
|
if (role == AircraftVariantCountRole) {
|
||||||
return item->variants.count();
|
return item->variants.count();
|
||||||
|
@ -634,7 +627,7 @@ QVariant AircraftItemModel::dataFromItem(AircraftItemPtr item, quint32 variantIn
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role == AircraftThumbnailSizeRole) {
|
if (role == AircraftThumbnailSizeRole) {
|
||||||
return item->thumbnail().size();
|
return item->thumbnail(false).size();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((role >= AircraftVariantDescriptionRole) && (role < AircraftThumbnailRole)) {
|
if ((role >= AircraftVariantDescriptionRole) && (role < AircraftThumbnailRole)) {
|
||||||
|
@ -642,10 +635,10 @@ QVariant AircraftItemModel::dataFromItem(AircraftItemPtr item, quint32 variantIn
|
||||||
return item->variants.at(variantIndex)->description;
|
return item->variants.at(variantIndex)->description;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (variantIndex) {
|
if (state.variant) {
|
||||||
if (variantIndex <= static_cast<quint32>(item->variants.count())) {
|
if (state.variant <= static_cast<quint32>(item->variants.count())) {
|
||||||
// show the selected variant
|
// show the selected variant
|
||||||
item = item->variants.at(variantIndex - 1);
|
item = item->variants.at(state.variant - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -690,7 +683,7 @@ QVariant AircraftItemModel::dataFromItem(AircraftItemPtr item, quint32 variantIn
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant AircraftItemModel::dataFromPackage(const PackageRef& item, quint32 variantIndex, int role) const
|
QVariant AircraftItemModel::dataFromPackage(const PackageRef& item, const DelegateState& state, int role) const
|
||||||
{
|
{
|
||||||
if (role == Qt::DecorationRole) {
|
if (role == Qt::DecorationRole) {
|
||||||
role = AircraftThumbnailRole; // use first thumbnail
|
role = AircraftThumbnailRole; // use first thumbnail
|
||||||
|
@ -706,7 +699,7 @@ QVariant AircraftItemModel::dataFromPackage(const PackageRef& item, quint32 vari
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role == Qt::DisplayRole) {
|
if (role == Qt::DisplayRole) {
|
||||||
QString desc = QString::fromStdString(item->nameForVariant(variantIndex));
|
QString desc = QString::fromStdString(item->nameForVariant(state.variant));
|
||||||
if (desc.isEmpty()) {
|
if (desc.isEmpty()) {
|
||||||
desc = tr("Missing description for: %1").arg(QString::fromStdString(item->id()));
|
desc = tr("Missing description for: %1").arg(QString::fromStdString(item->id()));
|
||||||
}
|
}
|
||||||
|
@ -717,7 +710,7 @@ QVariant AircraftItemModel::dataFromPackage(const PackageRef& item, quint32 vari
|
||||||
return QString::fromStdString(i->primarySetPath().utf8Str());
|
return QString::fromStdString(i->primarySetPath().utf8Str());
|
||||||
}
|
}
|
||||||
} else if (role == AircraftPackageIdRole) {
|
} else if (role == AircraftPackageIdRole) {
|
||||||
return QString::fromStdString(item->variants()[variantIndex]);
|
return QString::fromStdString(item->variants()[state.variant]);
|
||||||
} else if (role == AircraftPackageStatusRole) {
|
} else if (role == AircraftPackageStatusRole) {
|
||||||
InstallRef i = item->existingInstall();
|
InstallRef i = item->existingInstall();
|
||||||
if (i.valid()) {
|
if (i.valid()) {
|
||||||
|
@ -740,12 +733,15 @@ QVariant AircraftItemModel::dataFromPackage(const PackageRef& item, quint32 vari
|
||||||
// including the primary. Hence the -1 term.
|
// including the primary. Hence the -1 term.
|
||||||
return static_cast<quint32>(item->variants().size() - 1);
|
return static_cast<quint32>(item->variants().size() - 1);
|
||||||
} else if (role == AircraftThumbnailSizeRole) {
|
} else if (role == AircraftThumbnailSizeRole) {
|
||||||
QPixmap pm = packageThumbnail(item, 0, false).value<QPixmap>();
|
QPixmap pm = packageThumbnail(item, state, false).value<QPixmap>();
|
||||||
if (pm.isNull())
|
if (pm.isNull())
|
||||||
return QSize(STANDARD_THUMBNAIL_WIDTH, STANDARD_THUMBNAIL_HEIGHT);
|
return QSize(STANDARD_THUMBNAIL_WIDTH, STANDARD_THUMBNAIL_HEIGHT);
|
||||||
return pm.size();
|
return pm.size();
|
||||||
} else if (role >= AircraftThumbnailRole) {
|
} else if (role >= AircraftThumbnailRole) {
|
||||||
return packageThumbnail(item , role - AircraftThumbnailRole);
|
DelegateState changedState(state);
|
||||||
|
// override the current thumbnail as required
|
||||||
|
changedState.thumbnail = (role - AircraftThumbnailRole);
|
||||||
|
return packageThumbnail(item, changedState);
|
||||||
} else if (role == AircraftAuthorsRole) {
|
} else if (role == AircraftAuthorsRole) {
|
||||||
SGPropertyNode* authors = item->properties()->getChild("author");
|
SGPropertyNode* authors = item->properties()->getChild("author");
|
||||||
if (authors) {
|
if (authors) {
|
||||||
|
@ -756,7 +752,7 @@ QVariant AircraftItemModel::dataFromPackage(const PackageRef& item, quint32 vari
|
||||||
} else if (role == AircraftPackageSizeRole) {
|
} else if (role == AircraftPackageSizeRole) {
|
||||||
return static_cast<int>(item->fileSizeBytes());
|
return static_cast<int>(item->fileSizeBytes());
|
||||||
} else if (role == AircraftURIRole) {
|
} else if (role == AircraftURIRole) {
|
||||||
return QUrl("package:" + QString::fromStdString(item->qualifiedVariantId(variantIndex)));
|
return QUrl("package:" + QString::fromStdString(item->qualifiedVariantId(state.variant)));
|
||||||
} else if (role == AircraftHasRatingsRole) {
|
} else if (role == AircraftHasRatingsRole) {
|
||||||
return item->properties()->hasChild("rating");
|
return item->properties()->hasChild("rating");
|
||||||
} else if ((role >= AircraftRatingRole) && (role < AircraftVariantDescriptionRole)) {
|
} else if ((role >= AircraftRatingRole) && (role < AircraftVariantDescriptionRole)) {
|
||||||
|
@ -771,14 +767,14 @@ QVariant AircraftItemModel::dataFromPackage(const PackageRef& item, quint32 vari
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant AircraftItemModel::packageThumbnail(PackageRef p, int index, bool download) const
|
QVariant AircraftItemModel::packageThumbnail(PackageRef p, const DelegateState& ds, bool download) const
|
||||||
{
|
{
|
||||||
const string_list& thumbnails(p->thumbnailUrls());
|
const string_list& thumbnails(p->thumbnailUrls());
|
||||||
if (index >= static_cast<int>(thumbnails.size())) {
|
if (ds.thumbnail >= static_cast<int>(thumbnails.size())) {
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string thumbnailUrl = thumbnails.at(index);
|
std::string thumbnailUrl = thumbnails.at(ds.thumbnail);
|
||||||
QString urlQString(QString::fromStdString(thumbnailUrl));
|
QString urlQString(QString::fromStdString(thumbnailUrl));
|
||||||
if (m_thumbnailPixmapCache.contains(urlQString)) {
|
if (m_thumbnailPixmapCache.contains(urlQString)) {
|
||||||
// cache hit, easy
|
// cache hit, easy
|
||||||
|
@ -792,7 +788,7 @@ QVariant AircraftItemModel::packageThumbnail(PackageRef p, int index, bool downl
|
||||||
const string_list& thumbNames(p->thumbnails());
|
const string_list& thumbNames(p->thumbnails());
|
||||||
if (!thumbNames.empty()) {
|
if (!thumbNames.empty()) {
|
||||||
SGPath path(ex->path());
|
SGPath path(ex->path());
|
||||||
path.append(p->thumbnails()[index]);
|
path.append(p->thumbnails()[ds.thumbnail]);
|
||||||
if (path.exists()) {
|
if (path.exists()) {
|
||||||
QPixmap pix;
|
QPixmap pix;
|
||||||
pix.load(QString::fromStdString(path.utf8Str()));
|
pix.load(QString::fromStdString(path.utf8Str()));
|
||||||
|
@ -809,20 +805,31 @@ QVariant AircraftItemModel::packageThumbnail(PackageRef p, int index, bool downl
|
||||||
if (download) {
|
if (download) {
|
||||||
m_packageRoot->requestThumbnailData(thumbnailUrl);
|
m_packageRoot->requestThumbnailData(thumbnailUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AircraftItemModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
bool AircraftItemModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||||
{
|
{
|
||||||
int row = index.row();
|
int row = index.row();
|
||||||
|
int newValue = value.toInt();
|
||||||
|
|
||||||
if (role == AircraftVariantRole) {
|
if (role == AircraftVariantRole) {
|
||||||
if (row >= m_activeVariant.size()) {
|
if (m_delegateStates[row].variant == newValue) {
|
||||||
row -= m_activeVariant.size();
|
return true;
|
||||||
m_packageVariant[row] = value.toInt();
|
|
||||||
} else {
|
|
||||||
m_activeVariant[row] = value.toInt();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_delegateStates[row].variant = newValue;
|
||||||
|
emit dataChanged(index, index);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role == AircraftCurrentThumbnailRole) {
|
||||||
|
if (m_delegateStates[row].thumbnail == newValue) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_delegateStates[row].thumbnail = newValue;
|
||||||
emit dataChanged(index, index);
|
emit dataChanged(index, index);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -885,8 +892,9 @@ void AircraftItemModel::onScanResults()
|
||||||
|
|
||||||
// default variants in all cases
|
// default variants in all cases
|
||||||
for (int i=0; i< newItems.count(); ++i) {
|
for (int i=0; i< newItems.count(); ++i) {
|
||||||
m_activeVariant.append(0);
|
m_delegateStates.insert(firstRow + i, DelegateState());
|
||||||
}
|
}
|
||||||
|
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,7 @@ const int AircraftURIRole = Qt::UserRole + 14;
|
||||||
const int AircraftThumbnailSizeRole = Qt::UserRole + 15;
|
const int AircraftThumbnailSizeRole = Qt::UserRole + 15;
|
||||||
const int AircraftIsHelicopterRole = Qt::UserRole + 16;
|
const int AircraftIsHelicopterRole = Qt::UserRole + 16;
|
||||||
const int AircraftIsSeaplaneRole = Qt::UserRole + 17;
|
const int AircraftIsSeaplaneRole = Qt::UserRole + 17;
|
||||||
|
const int AircraftCurrentThumbnailRole = Qt::UserRole + 18;
|
||||||
|
|
||||||
const int AircraftRatingRole = Qt::UserRole + 100;
|
const int AircraftRatingRole = Qt::UserRole + 100;
|
||||||
const int AircraftVariantDescriptionRole = Qt::UserRole + 200;
|
const int AircraftVariantDescriptionRole = Qt::UserRole + 200;
|
||||||
|
@ -73,7 +74,7 @@ struct AircraftItem
|
||||||
|
|
||||||
void toDataStream(QDataStream& ds) const;
|
void toDataStream(QDataStream& ds) const;
|
||||||
|
|
||||||
QPixmap thumbnail() const;
|
QPixmap thumbnail(bool loadIfRequired = true) const;
|
||||||
|
|
||||||
bool excluded;
|
bool excluded;
|
||||||
QString path;
|
QString path;
|
||||||
|
@ -163,12 +164,23 @@ private slots:
|
||||||
private:
|
private:
|
||||||
friend class PackageDelegate;
|
friend class PackageDelegate;
|
||||||
|
|
||||||
QVariant dataFromItem(AircraftItemPtr item, quint32 variantIndex, int role) const;
|
/**
|
||||||
|
* struct to record persistent state from the item-delegate, about the
|
||||||
|
* currently visible variant, thumbnail and similar.
|
||||||
|
*/
|
||||||
|
struct DelegateState
|
||||||
|
{
|
||||||
|
quint32 variant = 0;
|
||||||
|
quint32 thumbnail = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
QVariant dataFromItem(AircraftItemPtr item, const DelegateState& state, int role) const;
|
||||||
|
|
||||||
QVariant dataFromPackage(const simgear::pkg::PackageRef& item,
|
QVariant dataFromPackage(const simgear::pkg::PackageRef& item,
|
||||||
quint32 variantIndex, int role) const;
|
const DelegateState& state, int role) const;
|
||||||
|
|
||||||
QVariant packageThumbnail(simgear::pkg::PackageRef p, int index, bool download = true) const;
|
QVariant packageThumbnail(simgear::pkg::PackageRef p,
|
||||||
|
const DelegateState& state, bool download = true) const;
|
||||||
|
|
||||||
void abandonCurrentScan();
|
void abandonCurrentScan();
|
||||||
void refreshPackages();
|
void refreshPackages();
|
||||||
|
@ -182,9 +194,9 @@ private:
|
||||||
PackageDelegate* m_delegate = nullptr;
|
PackageDelegate* m_delegate = nullptr;
|
||||||
bool m_showMessageWidget = false;
|
bool m_showMessageWidget = false;
|
||||||
|
|
||||||
QVector<quint32> m_activeVariant;
|
|
||||||
QVector<quint32> m_packageVariant;
|
QVector<DelegateState> m_delegateStates;
|
||||||
|
|
||||||
simgear::pkg::RootRef m_packageRoot;
|
simgear::pkg::RootRef m_packageRoot;
|
||||||
simgear::pkg::PackageList m_packages;
|
simgear::pkg::PackageList m_packages;
|
||||||
|
|
||||||
|
|
|
@ -131,6 +131,7 @@
|
||||||
#include <Instrumentation/HUD/HUD.hxx>
|
#include <Instrumentation/HUD/HUD.hxx>
|
||||||
#include <Cockpit/cockpitDisplayManager.hxx>
|
#include <Cockpit/cockpitDisplayManager.hxx>
|
||||||
#include <Network/HTTPClient.hxx>
|
#include <Network/HTTPClient.hxx>
|
||||||
|
#include <Network/DNSClient.hxx>
|
||||||
#include <Network/fgcom.hxx>
|
#include <Network/fgcom.hxx>
|
||||||
#include <Network/http/httpd.hxx>
|
#include <Network/http/httpd.hxx>
|
||||||
#include <Include/version.h>
|
#include <Include/version.h>
|
||||||
|
@ -735,6 +736,7 @@ void fgCreateSubsystems(bool duringReset) {
|
||||||
if (!globals->get_subsystem<FGHTTPClient>()) {
|
if (!globals->get_subsystem<FGHTTPClient>()) {
|
||||||
globals->add_new_subsystem<FGHTTPClient>();
|
globals->add_new_subsystem<FGHTTPClient>();
|
||||||
}
|
}
|
||||||
|
globals->add_new_subsystem<FGDNSClient>();
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Initialize the flight model subsystem.
|
// Initialize the flight model subsystem.
|
||||||
|
|
|
@ -3,11 +3,13 @@ include(FlightGearComponent)
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
multiplaymgr.cxx
|
multiplaymgr.cxx
|
||||||
tiny_xdr.cxx
|
tiny_xdr.cxx
|
||||||
|
MPServerResolver.cxx
|
||||||
)
|
)
|
||||||
|
|
||||||
set(HEADERS
|
set(HEADERS
|
||||||
multiplaymgr.hxx
|
multiplaymgr.hxx
|
||||||
tiny_xdr.hxx
|
tiny_xdr.hxx
|
||||||
|
MPServerResolver.hxx
|
||||||
)
|
)
|
||||||
|
|
||||||
flightgear_component(MultiPlayer "${SOURCES}" "${HEADERS}")
|
flightgear_component(MultiPlayer "${SOURCES}" "${HEADERS}")
|
||||||
|
|
194
src/MultiPlayer/MPServerResolver.cxx
Normal file
194
src/MultiPlayer/MPServerResolver.cxx
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
MPServerResolver.cxx - mpserver names lookup via DNS
|
||||||
|
Written and copyright by Torsten Dreyer - November 2016
|
||||||
|
|
||||||
|
This file is part of FlightGear.
|
||||||
|
|
||||||
|
FlightGear is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
FlightGear is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with FlightGear. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/#include "MPServerResolver.hxx"
|
||||||
|
|
||||||
|
#include <Network/DNSClient.hxx>
|
||||||
|
#include <Main/fg_props.hxx>
|
||||||
|
#include <3rdparty/cjson/cJSON.h>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
using namespace simgear;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a name=value map from base64 encoded JSON string
|
||||||
|
*/
|
||||||
|
class MPServerProperties : public std::map<string, string> {
|
||||||
|
public:
|
||||||
|
MPServerProperties (string b64)
|
||||||
|
{
|
||||||
|
std::vector<unsigned char> b64dec;
|
||||||
|
simgear::strutils::decodeBase64 (b64, b64dec);
|
||||||
|
auto jsonString = string ((char*) b64dec.data (), b64dec.size ());
|
||||||
|
cJSON * json = ::cJSON_Parse (jsonString.c_str ());
|
||||||
|
if (json) {
|
||||||
|
for (int i = 0; i < ::cJSON_GetArraySize (json); i++) {
|
||||||
|
cJSON * cj = ::cJSON_GetArrayItem (json, i);
|
||||||
|
if (cj->string && cj->valuestring)
|
||||||
|
emplace (cj->string, cj->valuestring);
|
||||||
|
}
|
||||||
|
::cJSON_Delete (json);
|
||||||
|
} else {
|
||||||
|
SG_LOG(SG_NETWORK,SG_WARN, "MPServerResolver: Can't parse JSON string '" << jsonString << "'" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class MPServerResolver::MPServerResolver_priv {
|
||||||
|
public:
|
||||||
|
enum {
|
||||||
|
INIT, LOADING_SRV_RECORDS, LOAD_NEXT_TXT_RECORD, LOADING_TXT_RECORDS, DONE,
|
||||||
|
} _state = INIT;
|
||||||
|
|
||||||
|
FGDNSClient * _dnsClient = globals->get_subsystem<FGDNSClient> ();
|
||||||
|
DNS::Request_ptr _dnsRequest;
|
||||||
|
PropertyList _serverNodes;
|
||||||
|
PropertyList::const_iterator _serverNodes_it;
|
||||||
|
};
|
||||||
|
|
||||||
|
MPServerResolver::~MPServerResolver ()
|
||||||
|
{
|
||||||
|
delete _priv;
|
||||||
|
}
|
||||||
|
|
||||||
|
MPServerResolver::MPServerResolver () :
|
||||||
|
_priv (new MPServerResolver_priv ())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MPServerResolver::run ()
|
||||||
|
{
|
||||||
|
//SG_LOG(SG_NETWORK, SG_DEBUG, "MPServerResolver::run() with state=" << _priv->_state );
|
||||||
|
switch (_priv->_state) {
|
||||||
|
// First call - fire DNS lookup for SRV records
|
||||||
|
case MPServerResolver_priv::INIT:
|
||||||
|
if (!_priv->_dnsClient) {
|
||||||
|
SG_LOG(SG_NETWORK, SG_WARN, "MPServerResolver: DNS subsystem not available.");
|
||||||
|
onFailure ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_priv->_dnsRequest = new DNS::SRVRequest (_dnsName, _service, _protocol);
|
||||||
|
SG_LOG(SG_NETWORK, SG_INFO, "MPServerResolver: sending DNS request for " << _priv->_dnsRequest->getDn());
|
||||||
|
_priv->_dnsClient->makeRequest (_priv->_dnsRequest);
|
||||||
|
_priv->_state = MPServerResolver_priv::LOADING_SRV_RECORDS;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Check if response from SRV Query
|
||||||
|
case MPServerResolver_priv::LOADING_SRV_RECORDS:
|
||||||
|
if (_priv->_dnsRequest->isTimeout ()) {
|
||||||
|
SG_LOG(SG_NETWORK, SG_WARN, "Timeout waiting for DNS response. Query was: " << _priv->_dnsRequest->getDn());
|
||||||
|
onFailure ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_priv->_dnsRequest->isComplete ()) {
|
||||||
|
// Create a child node under _targetNode for each SRV entry of the response
|
||||||
|
SG_LOG(SG_NETWORK, SG_INFO, "MPServerResolver: got DNS response for " << _priv->_dnsRequest->getDn());
|
||||||
|
int idx = 0;
|
||||||
|
for (DNS::SRVRequest::SRV_ptr entry : dynamic_cast<DNS::SRVRequest*> (_priv->_dnsRequest.get ())->entries) {
|
||||||
|
SG_LOG(SG_NETWORK, SG_DEBUG,
|
||||||
|
"MPServerResolver: SRV " << entry->priority << " " << entry->weight << " " << entry->port << " " << entry->target);
|
||||||
|
if( 0 == entry->port ) {
|
||||||
|
SG_LOG(SG_NETWORK, SG_INFO, "MPServerResolver: Skipping offline host " << entry->target );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
SGPropertyNode * serverNode = _targetNode->getNode ("server", idx++, true);
|
||||||
|
serverNode->getNode ("hostname", true)->setStringValue (entry->target);
|
||||||
|
serverNode->getNode ("priority", true)->setIntValue (entry->priority);
|
||||||
|
serverNode->getNode ("weight", true)->setIntValue (entry->weight);
|
||||||
|
serverNode->getNode ("port", true)->setIntValue (entry->port);
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepare an iterator over the server-nodes to be used later when loading the TXT records
|
||||||
|
_priv->_serverNodes = _targetNode->getChildren ("server");
|
||||||
|
_priv->_serverNodes_it = _priv->_serverNodes.begin ();
|
||||||
|
if (_priv->_serverNodes_it == _priv->_serverNodes.end ()) {
|
||||||
|
// No SRV records found - flag failure
|
||||||
|
SG_LOG(SG_NETWORK, SG_WARN, "MPServerResolver: no multiplayer servers defined via DNS");
|
||||||
|
onFailure ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_priv->_state = MPServerResolver_priv::LOAD_NEXT_TXT_RECORD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// get the next TXT record
|
||||||
|
case MPServerResolver_priv::LOAD_NEXT_TXT_RECORD:
|
||||||
|
if (_priv->_serverNodes_it == _priv->_serverNodes.end ()) {
|
||||||
|
// we are done with all servers
|
||||||
|
_priv->_state = MPServerResolver_priv::DONE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// send the DNS query for the hostnames TXT record
|
||||||
|
_priv->_dnsRequest = new DNS::TXTRequest ((*_priv->_serverNodes_it)->getStringValue ("hostname"));
|
||||||
|
SG_LOG(SG_NETWORK, SG_INFO, "MPServerResolver: sending DNS request for " << _priv->_dnsRequest->getDn());
|
||||||
|
_priv->_dnsClient->makeRequest (_priv->_dnsRequest);
|
||||||
|
_priv->_state = MPServerResolver_priv::LOADING_TXT_RECORDS;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// check if response for TXT query
|
||||||
|
case MPServerResolver_priv::LOADING_TXT_RECORDS:
|
||||||
|
if (_priv->_dnsRequest->isTimeout ()) {
|
||||||
|
// on timeout, try proceeding with next server
|
||||||
|
SG_LOG(SG_NETWORK, SG_WARN, "Timeout waiting for DNS response. Query was: " << _priv->_dnsRequest->getDn());
|
||||||
|
_priv->_state = MPServerResolver_priv::LOAD_NEXT_TXT_RECORD;
|
||||||
|
++_priv->_serverNodes_it;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (_priv->_dnsRequest->isComplete ()) {
|
||||||
|
SG_LOG(SG_NETWORK, SG_INFO, "MPServerResolver: got DNS response for " << _priv->_dnsRequest->getDn());
|
||||||
|
// DNS::TXTRequest automatically extracts name=value entries for us, lets retrieve them
|
||||||
|
auto attributes = dynamic_cast<DNS::TXTRequest*> (_priv->_dnsRequest.get ())->attributes;
|
||||||
|
auto mpserverAttribute = attributes["flightgear-mpserver"];
|
||||||
|
if (!mpserverAttribute.empty ()) {
|
||||||
|
// we are only interested in the 'flightgear-mpserver=something' entry, this is a base64 encoded
|
||||||
|
// JSON string, convert this into a map<string,string>
|
||||||
|
MPServerProperties mpserverProperties (mpserverAttribute);
|
||||||
|
for (auto prop : mpserverProperties) {
|
||||||
|
// and store each as a node under our servers node.
|
||||||
|
SG_LOG(SG_NETWORK, SG_DEBUG, "MPServerResolver: TXT record attribute " << prop.first << "=" << prop.second);
|
||||||
|
// sanitize property name, don't allow dots or forward slash
|
||||||
|
auto propertyName = prop.first;
|
||||||
|
std::replace( propertyName.begin(), propertyName.end(), '.', '_');
|
||||||
|
std::replace( propertyName.begin(), propertyName.end(), '/', '_');
|
||||||
|
(*_priv->_serverNodes_it)->setStringValue (propertyName, prop.second);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SG_LOG(SG_NETWORK, SG_INFO, "MPServerResolver: TXT record attributes empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
// procede with the net node
|
||||||
|
++_priv->_serverNodes_it;
|
||||||
|
_priv->_state = MPServerResolver_priv::LOAD_NEXT_TXT_RECORD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MPServerResolver_priv::DONE:
|
||||||
|
onSuccess ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Relinguish control, call me back on the next frame
|
||||||
|
globals->get_event_mgr ()->addEvent ("MPServerResolver_update", this, &MPServerResolver::run, .0);
|
||||||
|
}
|
||||||
|
|
83
src/MultiPlayer/MPServerResolver.hxx
Normal file
83
src/MultiPlayer/MPServerResolver.hxx
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
MPServerResolver.hxx - mpserver names lookup via DNS
|
||||||
|
Written and copyright by Torsten Dreyer - November 2016
|
||||||
|
|
||||||
|
This file is part of FlightGear.
|
||||||
|
|
||||||
|
FlightGear is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
FlightGear is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with FlightGear. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#ifndef __FG_MPSERVERRESOLVER_HXX
|
||||||
|
#define __FG_MPSERVERRESOLVER_HXX
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <simgear/props/props.hxx>
|
||||||
|
|
||||||
|
class MPServerResolver {
|
||||||
|
public:
|
||||||
|
MPServerResolver();
|
||||||
|
virtual ~MPServerResolver();
|
||||||
|
void run();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the target property where the server-list gets stored
|
||||||
|
*
|
||||||
|
* \param value the property node to use as a target
|
||||||
|
*/
|
||||||
|
void setTarget( SGPropertyNode_ptr value ) { _targetNode = value; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the dns domain name to query. This could be either a full qualified name including the
|
||||||
|
* service and the protocol like _fgms._udp.flightgear.org or just the domain name like
|
||||||
|
* flightgear.org. Use setService() and setProtocol() in the latter case.
|
||||||
|
*
|
||||||
|
* \param value the dnsname to use for the query.
|
||||||
|
*/
|
||||||
|
void setDnsName( const std::string & value ) { _dnsName = value; }
|
||||||
|
|
||||||
|
/** Set the service name to use for the query. Don't add the underscore, this gets added
|
||||||
|
* automatically. This builds the fully qualified DNS name to query, together with
|
||||||
|
* setProtocol() and setDnsName().
|
||||||
|
*
|
||||||
|
* \param value the service name to use for the query sans the leading underscore
|
||||||
|
*/
|
||||||
|
void setService( const std::string & value ) { _service = value; }
|
||||||
|
|
||||||
|
/** Set the protocol name to use for the query. Don't add the underscore, this gets added
|
||||||
|
* automatically. This builds the fully qualified DNS name to query, together with
|
||||||
|
* setService() and setDnsName().
|
||||||
|
*
|
||||||
|
* \param value the protocol name to use for the query sans the leading underscore
|
||||||
|
*/
|
||||||
|
void setProtocol( const std::string & value ) { _protocol = value; }
|
||||||
|
|
||||||
|
/** Handler to be called if the resolver process finishes with success. Does nothing by
|
||||||
|
* default and should be overridden be the user.
|
||||||
|
*/
|
||||||
|
virtual void onSuccess() {};
|
||||||
|
|
||||||
|
/** Handler to be called if the resolver process terminates with an error. Does nothing by
|
||||||
|
* default and should be overridden be the user.
|
||||||
|
*/
|
||||||
|
virtual void onFailure() {};
|
||||||
|
|
||||||
|
private:
|
||||||
|
class MPServerResolver_priv;
|
||||||
|
std::string _dnsName;
|
||||||
|
std::string _service;
|
||||||
|
std::string _protocol;
|
||||||
|
SGPropertyNode_ptr _targetNode;
|
||||||
|
MPServerResolver_priv * _priv;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __FG_MPSERVERRESOLVER_HXX
|
|
@ -41,14 +41,14 @@
|
||||||
#include <simgear/debug/logstream.hxx>
|
#include <simgear/debug/logstream.hxx>
|
||||||
#include <simgear/props/props.hxx>
|
#include <simgear/props/props.hxx>
|
||||||
#include <simgear/structure/commands.hxx>
|
#include <simgear/structure/commands.hxx>
|
||||||
|
#include <simgear/structure/event_mgr.hxx>
|
||||||
|
|
||||||
#include <AIModel/AIManager.hxx>
|
#include <AIModel/AIManager.hxx>
|
||||||
#include <AIModel/AIMultiplayer.hxx>
|
#include <AIModel/AIMultiplayer.hxx>
|
||||||
#include <Main/fg_props.hxx>
|
#include <Main/fg_props.hxx>
|
||||||
#include <Network/RemoteXMLRequest.hxx>
|
|
||||||
#include <Network/HTTPClient.hxx>
|
|
||||||
#include "multiplaymgr.hxx"
|
#include "multiplaymgr.hxx"
|
||||||
#include "mpmessages.hxx"
|
#include "mpmessages.hxx"
|
||||||
|
#include "MPServerResolver.hxx"
|
||||||
#include <FDM/flightProperties.hxx>
|
#include <FDM/flightProperties.hxx>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
@ -428,40 +428,60 @@ static bool do_multiplayer_disconnect(const SGPropertyNode * arg) {
|
||||||
// none
|
// none
|
||||||
//
|
//
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
static bool do_multiplayer_refreshserverlist(const SGPropertyNode * arg) {
|
|
||||||
FGMultiplayMgr * self = (FGMultiplayMgr*) globals->get_subsystem("mp");
|
|
||||||
if (!self) {
|
|
||||||
SG_LOG(SG_NETWORK, SG_WARN, "Multiplayer subsystem not available.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
FGHTTPClient* http = globals->get_subsystem<FGHTTPClient>();
|
static bool
|
||||||
if (!http) {
|
do_multiplayer_refreshserverlist (const SGPropertyNode * arg)
|
||||||
SG_LOG(SG_IO, SG_ALERT,
|
{
|
||||||
"do_multiplayer.refreshserverlist: HTTP client not running");
|
using namespace simgear;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
string url(
|
FGMultiplayMgr * self = (FGMultiplayMgr*) globals->get_subsystem ("mp");
|
||||||
fgGetString("/sim/multiplay/serverlist-url",
|
if (!self) {
|
||||||
"http://liveries.flightgear.org/mpstatus/mpservers.xml"));
|
SG_LOG(SG_NETWORK, SG_WARN, "Multiplayer subsystem not available.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (url.empty()) {
|
// MPServerResolver implementation to fill the mp server list
|
||||||
SG_LOG(SG_IO, SG_ALERT,
|
// deletes itself when done
|
||||||
"do_multiplayer.refreshserverlist: no URL given");
|
class MyMPServerResolver : public MPServerResolver {
|
||||||
return false;
|
public:
|
||||||
}
|
MyMPServerResolver () :
|
||||||
|
MPServerResolver ()
|
||||||
|
{
|
||||||
|
setTarget (fgGetNode ("/sim/multiplay/server-list", true));
|
||||||
|
setDnsName (fgGetString ("/sim/multiplay/dns/query-dn", "flightgear.org"));
|
||||||
|
setService (fgGetString ("/sim/multiplay/dns/query-srv-service", "fgms"));
|
||||||
|
setProtocol (fgGetString ("/sim/multiplay/dns/query-srv-protocol", "udp"));
|
||||||
|
_completeNode->setBoolValue (false);
|
||||||
|
_failureNode->setBoolValue (false);
|
||||||
|
}
|
||||||
|
|
||||||
SGPropertyNode *targetnode = fgGetNode("/sim/multiplay/server-list", true);
|
~MyMPServerResolver ()
|
||||||
SGPropertyNode *completeNode = fgGetNode("/sim/multiplay/got-servers", true);
|
{
|
||||||
SGPropertyNode *failureNode = fgGetNode("/sim/multiplay/get-servers-failure", true);
|
}
|
||||||
RemoteXMLRequest* req = new RemoteXMLRequest(url, targetnode);
|
|
||||||
req->setCompletionProp(completeNode);
|
virtual void
|
||||||
req->setFailedProp(failureNode);
|
onSuccess ()
|
||||||
completeNode->setBoolValue(false);
|
{
|
||||||
failureNode->setBoolValue(false);
|
SG_LOG(SG_NETWORK, SG_DEBUG, "MyMPServerResolver: trigger success");
|
||||||
http->makeRequest(req);
|
_completeNode->setBoolValue (true);
|
||||||
return true;
|
delete this;
|
||||||
|
}
|
||||||
|
virtual void
|
||||||
|
onFailure ()
|
||||||
|
{
|
||||||
|
SG_LOG(SG_NETWORK, SG_DEBUG, "MyMPServerResolver: trigger failure");
|
||||||
|
_failureNode->setBoolValue (true);
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
SGPropertyNode *_completeNode = fgGetNode ("/sim/multiplay/got-servers", true);
|
||||||
|
SGPropertyNode *_failureNode = fgGetNode ("/sim/multiplay/get-servers-failure", true);
|
||||||
|
};
|
||||||
|
|
||||||
|
MyMPServerResolver * mpServerResolver = new MyMPServerResolver ();
|
||||||
|
mpServerResolver->run ();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -11,6 +11,7 @@ set(SOURCES
|
||||||
garmin.cxx
|
garmin.cxx
|
||||||
generic.cxx
|
generic.cxx
|
||||||
HTTPClient.cxx
|
HTTPClient.cxx
|
||||||
|
DNSClient.cxx
|
||||||
igc.cxx
|
igc.cxx
|
||||||
joyclient.cxx
|
joyclient.cxx
|
||||||
jsclient.cxx
|
jsclient.cxx
|
||||||
|
@ -39,6 +40,7 @@ set(HEADERS
|
||||||
garmin.hxx
|
garmin.hxx
|
||||||
generic.hxx
|
generic.hxx
|
||||||
HTTPClient.hxx
|
HTTPClient.hxx
|
||||||
|
DNSClient.hxx
|
||||||
igc.hxx
|
igc.hxx
|
||||||
joyclient.hxx
|
joyclient.hxx
|
||||||
jsclient.hxx
|
jsclient.hxx
|
||||||
|
|
71
src/Network/DNSClient.cxx
Normal file
71
src/Network/DNSClient.cxx
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
// HDNSClient.cxx -- Singleton DNS client object
|
||||||
|
//
|
||||||
|
// Written by James Turner, started April 2012.
|
||||||
|
// Based on HTTPClient from James Turner
|
||||||
|
// Copyright (C) 2012 James Turner
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License as
|
||||||
|
// published by the Free Software Foundation; either version 2 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful, but
|
||||||
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
#include "DNSClient.hxx"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#include <Main/fg_props.hxx>
|
||||||
|
#include <Include/version.h>
|
||||||
|
|
||||||
|
#include <simgear/sg_inlines.h>
|
||||||
|
|
||||||
|
|
||||||
|
using namespace simgear;
|
||||||
|
|
||||||
|
FGDNSClient::FGDNSClient() :
|
||||||
|
_inited(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FGDNSClient::~FGDNSClient()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGDNSClient::init()
|
||||||
|
{
|
||||||
|
if (_inited) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_dns.reset( new simgear::DNS::Client() );
|
||||||
|
|
||||||
|
_inited = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGDNSClient::postinit()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGDNSClient::shutdown()
|
||||||
|
{
|
||||||
|
_dns.reset();
|
||||||
|
_inited = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGDNSClient::update(double)
|
||||||
|
{
|
||||||
|
_dns->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGDNSClient::makeRequest(const simgear::DNS::Request_ptr& req)
|
||||||
|
{
|
||||||
|
_dns->makeRequest(req);
|
||||||
|
}
|
52
src/Network/DNSClient.hxx
Normal file
52
src/Network/DNSClient.hxx
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
// DNSClient.hxx -- Singleton DNS client object
|
||||||
|
//
|
||||||
|
// Written by Torsten Dreyer, started November 2016.
|
||||||
|
// Based on HTTPClient from James Turner
|
||||||
|
// Copyright (C) 2012 James Turner
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License as
|
||||||
|
// published by the Free Software Foundation; either version 2 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful, but
|
||||||
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
#ifndef FG_DNS_CLIENT_HXX
|
||||||
|
#define FG_DNS_CLIENT_HXX
|
||||||
|
|
||||||
|
#include <simgear/structure/subsystem_mgr.hxx>
|
||||||
|
#include <simgear/io/DNSClient.hxx>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class FGDNSClient : public SGSubsystem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FGDNSClient();
|
||||||
|
virtual ~FGDNSClient();
|
||||||
|
|
||||||
|
void makeRequest(const simgear::DNS::Request_ptr& req);
|
||||||
|
|
||||||
|
// simgear::HTTP::Client* client() { return _http.get(); }
|
||||||
|
// simgear::HTTP::Client const* client() const { return _http.get(); }
|
||||||
|
|
||||||
|
virtual void init();
|
||||||
|
virtual void postinit();
|
||||||
|
virtual void shutdown();
|
||||||
|
virtual void update(double);
|
||||||
|
|
||||||
|
static const char* subsystemName() { return "dns"; }
|
||||||
|
private:
|
||||||
|
bool _inited;
|
||||||
|
std::unique_ptr<simgear::DNS::Client> _dns;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FG_DNS_CLIENT_HXX
|
||||||
|
|
||||||
|
|
2
version
2
version
|
@ -1 +1 @@
|
||||||
2016.4.0
|
2017.1.0
|
||||||
|
|
Loading…
Reference in a new issue