diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index e42ed8fd4..f17db51e5 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -22,7 +22,9 @@ env:
bitrock_version: qt-professional-24.7.0
bitrock_url: https://releases.installbuilder.com/installbuilder
externals: swift-project/externals
- externals_sha: dfe49bbeb8f0ca664afa293ad3f454cffe751acf
+ externals_sha: e1f1743ba159e11b0c065ea8f1ae1a0e91e3bf39
+
+
use_externals: ${{ secrets.EXTERNALS_PAT != '' }}
jobs:
diff --git a/.gitignore b/.gitignore
index 759db19d5..04e080afc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -52,3 +52,5 @@ cmake-build-*/
/dist/
CMakeUserPresets.json
/third_party/externals
+/src/plugins/simulator/msfs2024/simulatormsfs2024.cpp_
+/src/plugins/simulator/msfs2024/simulatormsfs2024.h_
diff --git a/src/core/db/databaseutils.cpp b/src/core/db/databaseutils.cpp
index fca32ae61..d40321087 100644
--- a/src/core/db/databaseutils.cpp
+++ b/src/core/db/databaseutils.cpp
@@ -75,6 +75,7 @@ namespace swift::core::db
dbModelModified.updateMissingParts(model);
dbModelModified.setDistributorOrder(distributorOrder);
dbModelModified.setSimulator(dbModel.getSimulator()); // DB simulator settings have priority
+ dbModelModified.setModelLivery(model.getModelLivery()); // keep local livery settings msfs2024
return dbModelModified;
}
@@ -88,6 +89,7 @@ namespace swift::core::db
{
if (modified) { *modified = true; }
consolidatedModel.setLivery(dbLivery);
+ consolidatedModel.setModelLivery(model.getModelLivery()); // keep local livery settings msfs2024
}
}
if (!consolidatedModel.getAircraftIcaoCode().hasValidDbKey() && consolidatedModel.hasAircraftDesignator())
@@ -99,6 +101,7 @@ namespace swift::core::db
{
if (modified) { *modified = true; }
consolidatedModel.setAircraftIcaoCode(dbIcao);
+ consolidatedModel.setModelLivery(model.getModelLivery()); // keep local livery settings msfs2024
}
}
@@ -108,6 +111,7 @@ namespace swift::core::db
{
if (modified) { *modified = true; }
consolidatedModel.setDistributor(dbDistributor);
+ consolidatedModel.setModelLivery(model.getModelLivery()); // keep local livery settings msfs2024
}
consolidatedModel.updateLocalFileNames(model);
consolidatedModel.setDistributorOrder(distributorOrder);
diff --git a/src/core/modelsetbuilder.cpp b/src/core/modelsetbuilder.cpp
index 56c457b0c..93b717fbb 100644
--- a/src/core/modelsetbuilder.cpp
+++ b/src/core/modelsetbuilder.cpp
@@ -49,8 +49,9 @@ namespace swift::core
}
else
{
- // without any information we can not use them
- modelSet = modelSet.findWithKnownAircraftDesignator();
+ if (!options.testFlag(ShowAllInstalledModells))
+ // without any information we can not use them
+ modelSet = modelSet.findWithKnownAircraftDesignator();
}
// Include only
diff --git a/src/core/modelsetbuilder.h b/src/core/modelsetbuilder.h
index c85137707..31dfe0518 100644
--- a/src/core/modelsetbuilder.h
+++ b/src/core/modelsetbuilder.h
@@ -34,7 +34,8 @@ namespace swift::core
OnlyDbIcaoCodes = 1 << 2,
Incremental = 1 << 3,
SortByDistributors = 1 << 4,
- ConsolidateWithDb = 1 << 5
+ ConsolidateWithDb = 1 << 5,
+ ShowAllInstalledModells = 1 << 6,
};
Q_DECLARE_FLAGS(Builder, BuilderFlag)
diff --git a/src/gui/components/dbownmodelscomponent.cpp b/src/gui/components/dbownmodelscomponent.cpp
index 6b94c12c2..3ab117f41 100644
--- a/src/gui/components/dbownmodelscomponent.cpp
+++ b/src/gui/components/dbownmodelscomponent.cpp
@@ -280,10 +280,10 @@ namespace swift::gui::components
{
QMessageBox msgBox(QMessageBox::Question, "Reload models from disk",
QStringLiteral("Completely reload '%1' models from disk?").arg(simulator.toQString(true)),
- QMessageBox::Ok | QMessageBox::Cancel, this);
+ QMessageBox::Yes | QMessageBox::No, this);
msgBox.setDefaultButton(QMessageBox::Cancel);
const QMessageBox::StandardButton reply = static_cast(msgBox.exec());
- if (reply != QMessageBox::Ok) { return; }
+ if (reply != QMessageBox::Yes) { return; }
this->requestSimulatorModels(simulator, IAircraftModelLoader::InBackgroundNoCache);
}
@@ -335,6 +335,21 @@ namespace swift::gui::components
ui->tvp_OwnAircraftModels->updateContainerMaybeAsync(this->getOwnModels());
}
+ // TODO TZ this is a stub for SimConnect loading
+ void CDbOwnModelsComponent::loadInstalledModelsSimConnect(const CSimulatorInfo &simulator,
+ IAircraftModelLoader::LoadMode mode,
+ const QStringList &modelDirectories)
+ {
+ Q_UNUSED(mode);
+ Q_UNUSED(modelDirectories);
+
+ using namespace std::chrono_literals;
+ const CStatusMessage msg = CLogMessage(this).info(u"Start loading models for %1") << simulator.toQString();
+ this->showOverlayHTMLMessage(msg, 2s);
+
+ return;
+ }
+
void CDbOwnModelsComponent::loadInstalledModels(const CSimulatorInfo &simulator,
IAircraftModelLoader::LoadMode mode,
const QStringList &modelDirectories)
@@ -515,7 +530,11 @@ namespace swift::gui::components
IAircraftModelLoader::LoadMode mode,
const QStringList &modelDirectories)
{
- this->loadInstalledModels(simulator, mode, modelDirectories);
+ // At this point, we switch how the models should be loaded: SimConnect or classic file search
+ if (simulator.isMSFS2024())
+ this->loadInstalledModelsSimConnect(simulator, mode, modelDirectories);
+ else
+ this->loadInstalledModels(simulator, mode, modelDirectories);
}
void CDbOwnModelsComponent::requestSimulatorModelsWithCacheInBackground(const CSimulatorInfo &simulator)
diff --git a/src/gui/components/dbownmodelscomponent.h b/src/gui/components/dbownmodelscomponent.h
index b47051350..6854ac3e6 100644
--- a/src/gui/components/dbownmodelscomponent.h
+++ b/src/gui/components/dbownmodelscomponent.h
@@ -153,6 +153,8 @@ namespace swift::gui
void ownModelsSimulatorChanged(const swift::misc::simulation::CSimulatorInfo &simulator);
private:
+ static constexpr std::chrono::milliseconds OverlayMsgTimeout { 5000 }; //!< how long overlay is displayed
+
QScopedPointer ui;
swift::misc::simulation::IAircraftModelLoader *m_modelLoader =
nullptr; //!< read own aircraft models, aka models on disk
@@ -167,7 +169,12 @@ namespace swift::gui
//! Request own models
void requestOwnModelsUpdate();
- //! Load the models
+ //! Load the models via SimConnect
+ void loadInstalledModelsSimConnect(const swift::misc::simulation::CSimulatorInfo &simulator,
+ swift::misc::simulation::IAircraftModelLoader::LoadMode mode,
+ const QStringList &modelDirectories = {});
+
+ //! Load the models via disk
void loadInstalledModels(const swift::misc::simulation::CSimulatorInfo &simulator,
swift::misc::simulation::IAircraftModelLoader::LoadMode mode,
const QStringList &modelDirectories = {});
diff --git a/src/gui/components/mappingcomponent.ui b/src/gui/components/mappingcomponent.ui
index f06bb5f7d..da52efbf9 100644
--- a/src/gui/components/mappingcomponent.ui
+++ b/src/gui/components/mappingcomponent.ui
@@ -1,358 +1,359 @@
- CMappingComponent
-
-
-
- 0
- 0
- 376
- 293
-
-
-
- Mapping component
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
- -
-
-
- Qt::Vertical
-
-
-
- 0
+ CMappingComponent
+
+
+
+ 0
+ 0
+ 376
+ 293
+
+
+
+ Mapping component
+
+
+
+ 0
-
-
- Rendered aircraft
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
-
- 0
- 0
-
-
-
- QAbstractItemView::SingleSelection
-
-
- QAbstractItemView::SelectRows
-
-
- false
-
-
-
-
-
-
-
- Aircraft models
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
- -
-
-
- QAbstractItemView::SingleSelection
-
-
- QAbstractItemView::SelectRows
-
-
- false
-
-
-
-
-
-
-
- Statistics
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
- -
-
-
-
-
-
-
- Matching log
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
- -
-
-
-
-
-
-
-
-
- 16777215
- 80
-
+
+ 0
-
-
- 3
-
-
- 3
-
-
- 3
-
-
- 3
-
- -
-
-
- validate
-
+
+ 0
+
+
+ 0
+
+
-
+
+
+ Qt::Vertical
+
+
+
+ 0
+
+
+
+ Rendered aircraft
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ QAbstractItemView::SingleSelection
+
+
+ QAbstractItemView::SelectRows
+
+
+ false
+
+
+
+
+
+
+
+ Aircraft models
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ QAbstractItemView::SingleSelection
+
+
+ QAbstractItemView::SelectRows
+
+
+ false
+
+
+
+
+
+
+
+ Statistics
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+
+
+
+ Matching log
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+
+
+
+
+
+ 16777215
+ 80
+
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ validate
+
+
+
+ -
+
+
+ Icon
+
+
+
+ -
+
+
+ do all matchings again
+
+
+ re-match
+
+
+
+ -
+
+
+ save selected model
+
+
+ save
+
+
+
+ -
+
+
+ reset model by callsign
+
+
+ reset
+
+
+
+ :/pastel/icons/pastel/16/arrow-refresh.png:/pastel/icons/pastel/16/arrow-refresh.png
+
+
+
+
+ -
+
+
+ callsign of aircraft
+
+
+ 15
+
+
+ true
+
+
+ callsign
+
+
+
+ -
+
+
+ load model set
+
+
+ load model set
+
+
+
+ -
+
+
+ aircraft enabled
+
+
+ Enbl.
+
+
+
+ -
+
+
+
+ 50
+ 0
+
+
+
+
+ -
+
+
+
+ 0
+ 20
+
+
+
+
+ -
+
+
+ show / hide icon of aircraft
+
+
+
+
+
+ false
+
+
+
+ -
+
+
+ aircraft enabled/disable
+
+
+
+
+
+
+
+
-
- -
-
-
- Icon
-
-
-
- -
-
-
- do all matchings again
-
-
- re-match
-
-
-
- -
-
-
- save selected model
-
-
- save
-
-
-
- -
-
-
- reset model by callsign
-
-
- reset
-
-
-
- :/pastel/icons/pastel/16/arrow-refresh.png:/pastel/icons/pastel/16/arrow-refresh.png
-
-
-
- -
-
-
- callsign of aircraft
-
-
- 15
-
-
- true
-
-
- callsign
-
-
-
- -
-
-
- load model set
-
-
- load model set
-
-
-
- -
-
-
- aircraft enabled
-
-
- Enbl.
-
-
-
- -
-
-
-
- 50
- 0
-
-
-
-
- -
-
-
-
- 0
- 20
-
-
-
-
- -
-
-
- show / hide icon of aircraft
-
-
-
-
-
- false
-
-
-
- -
-
-
- aircraft enabled/disable
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- swift::gui::views::CAircraftModelView
- QTableView
- gui/views/aircraftmodelview.h
-
-
- swift::gui::components::CSimulatorSelector
- QFrame
- gui/components/simulatorselector.h
- 1
-
-
- swift::gui::views::CSimulatedAircraftView
- QTableView
- gui/views/simulatedaircraftview.h
-
-
- swift::gui::components::CModelMatcherLogComponent
- QFrame
- gui/components/modelmatcherlogcomponent.h
- 1
-
-
- swift::gui::components::CAircraftModelStringCompleter
- QFrame
- gui/components/aircraftmodelstringcompleter.h
- 1
-
-
- swift::gui::components::CMatchingStatisticsComponent
- QFrame
- gui/components/matchingstatisticscomponent.h
- 1
-
-
-
- tw_SpecializedViews
- tvp_RenderedAircraft
- cb_AircraftIconDisplayed
- pb_ValidateModelSet
- cb_AircraftEnabled
- le_Callsign
- pb_ResetAircraft
- pb_DoMatchingAgain
- pb_SaveAircraft
- tvp_AircraftModels
-
-
-
-
-
+
+
+
+
+
+ swift::gui::views::CAircraftModelView
+ QTableView
+ gui/views/aircraftmodelview.h
+
+
+ swift::gui::components::CSimulatorSelector
+ QFrame
+ gui/components/simulatorselector.h
+ 1
+
+
+ swift::gui::views::CSimulatedAircraftView
+ QTableView
+ gui/views/simulatedaircraftview.h
+
+
+ swift::gui::components::CModelMatcherLogComponent
+ QFrame
+ gui/components/modelmatcherlogcomponent.h
+ 1
+
+
+ swift::gui::components::CAircraftModelStringCompleter
+ QFrame
+ gui/components/aircraftmodelstringcompleter.h
+ 1
+
+
+ swift::gui::components::CMatchingStatisticsComponent
+ QFrame
+ gui/components/matchingstatisticscomponent.h
+ 1
+
+
+
+ tw_SpecializedViews
+ tvp_RenderedAircraft
+ cb_AircraftIconDisplayed
+ pb_ValidateModelSet
+ cb_AircraftEnabled
+ le_Callsign
+ pb_ResetAircraft
+ pb_DoMatchingAgain
+ pb_SaveAircraft
+ tvp_AircraftModels
+
+
+
+
+
diff --git a/src/gui/components/settingssimulatorbasicscomponent.cpp b/src/gui/components/settingssimulatorbasicscomponent.cpp
index 44577e175..25cd3d436 100644
--- a/src/gui/components/settingssimulatorbasicscomponent.cpp
+++ b/src/gui/components/settingssimulatorbasicscomponent.cpp
@@ -138,12 +138,16 @@ namespace swift::gui::components
s.setSimulatorDirectory(simulatorDir);
s.setModelDirectories(modelDirs);
s.setModelExcludeDirectories(relativeDirs);
+ s.setPropertyModelSet(ui->cb_LoadNewModelset->isChecked());
+ s.setPropertyWithDbEntry(ui->cb_WithDbEntry->isChecked());
+ s.setPropertyDistributorFiltered(ui->cb_DistributorFiltered->isChecked());
+
const CStatusMessageList msgs = m_settings.setAndValidateSettings(s, simulator);
if (msgs.isSuccess())
{
const CStatusMessage m = m_settings.setAndSaveSettings(s, simulator);
if (!m.isEmpty()) { CLogMessage::preformatted(m); }
- if (m.isSuccess()) { this->showOverlayHTMLMessage("Saved settings", 5s); }
+ if (m.isSuccess()) { this->showOverlayHTMLMessage("Saved settings", 2s); }
else { this->showOverlayMessage(m); }
m_unsavedChanges = m_unsavedChanges && !m.isSuccess(); // reset if success, but only if there were changes
@@ -227,6 +231,40 @@ namespace swift::gui::components
{
const CSimulatorInfo simulator(ui->comp_SimulatorSelector->getValue());
this->displaySettings(simulator);
+
+ // special handling for MSFS 2024 only visual changes
+ if (simulator == CSimulatorInfo::msfs2024())
+ {
+ ui->gb_ModelSet->setVisible(true);
+ ui->lbl_ExcludeDirectories->setText(QStringLiteral("modelstring exclude patterns:"));
+ ui->lbl_ModelDirectory->setText(QStringLiteral("modelstring filter patterns:"));
+ ui->lbl_ModelDirectory->setToolTip(
+ QStringLiteral("If the field is not empty, these patterns are used as a filter for the model string."));
+ ui->pb_AdjustModelDirectory->setVisible(false);
+ ui->pb_ExcludeFileDialog->setVisible(false);
+ ui->pb_ModelFileDialog->setVisible(false);
+ const QString html =
+ "
"These "
+ "filter settings are applied to the model string passed from the flight simulator to swift. If the "
+ "effect is unclear, start with the default settings!".
";
+ ui->lbl_ModelDirsInfo->setText(html);
+ }
+ else
+ {
+ ui->gb_ModelSet->setVisible(false);
+ ui->lbl_ExcludeDirectories->setText(QStringLiteral("Exclude directory patterns:"));
+ ui->lbl_ModelDirectory->setText(QStringLiteral("Model directories:"));
+ ui->lbl_ModelDirectory->setToolTip(QStringLiteral("remove redundant directories, fix file paths .."));
+ ui->pb_AdjustModelDirectory->setVisible(true);
+ ui->pb_ExcludeFileDialog->setVisible(true);
+ ui->pb_ModelFileDialog->setVisible(true);
+ const QString html =
+ "
"If you change the model "
+ "directories, you must update your model set. See documentation. ".
";
+ ui->lbl_ModelDirsInfo->setText(html);
+ }
+
this->displayDefaultValuesAsPlaceholder(simulator);
}
@@ -326,8 +364,27 @@ namespace swift::gui::components
void CSettingsSimulatorBasicsComponent::displaySettings(const CSimulatorInfo &simulator)
{
- this->displayExcludeDirectoryPatterns(m_settings.getModelExcludeDirectoryPatternsIfNotDefault(simulator));
- this->displayModelDirectories(m_settings.getModelDirectoriesIfNotDefault(simulator));
+ // if (simulator.isMSFS2024())
+ //{
+
+ // //CSpecializedSimulatorSettings settings = this->getSimulatorSettings(simulator.isMSFS2024());
+ // //CSimulatorSettings m_generic = settings.getGenericSettings();
+ // //QStringList excludePatterns = m_generic.getModelExcludeDirectoryPatterns();
+ // //QStringList filterPatterns = m_generic.getModelDirectories();
+
+ // //const CSimulatorSettings s = m_settings.getSettings(simulator);
+ // //const QString es = s.getSimulatorDirectory();
+ // ui->pte_ModelDirectories->clear();
+ // ui->pte_ExcludeDirectories->clear();
+
+ // //this->displayModelDirectories(excludePatterns);
+ // //this->displayExcludeDirectoryPatterns(filterPatterns);
+ //}
+ // else
+ //{
+ // this->displayModelDirectories(m_settings.getModelDirectoriesIfNotDefault(simulator));
+ // this->displayExcludeDirectoryPatterns(m_settings.getModelExcludeDirectoryPatternsIfNotDefault(simulator));
+ //}
// ui->le_SimulatorDirectory->setText(m_settings.getSimulatorDirectoryIfNotDefault(simulator));
// based on discussion here, always display:
@@ -339,26 +396,59 @@ namespace swift::gui::components
void CSettingsSimulatorBasicsComponent::displayDefaultValuesAsPlaceholder(const CSimulatorInfo &simulator)
{
const QString simDir = this->getFileBrowserSimulatorDirectory();
- ui->le_SimulatorDirectory->setPlaceholderText(simDir.isEmpty() ? "Simulator directory" : simDir);
+
+ // only real placeholder shut set as placeholder
+ simDir.isEmpty() ? (ui->le_SimulatorDirectory->setPlaceholderText("Simulator directory")) :
+ (ui->le_SimulatorDirectory->setText(simDir));
// we take the settings and update to latest sim.directory
CSpecializedSimulatorSettings settings = m_settings.getSpecializedSettings(simulator);
settings.setSimulatorDirectory(simDir);
- const QStringList m = settings.getModelDirectoriesFromSimulatorDirectoryOrDefault();
- if (m.isEmpty()) { ui->pte_ModelDirectories->setPlaceholderText("Model directories"); }
- else
- {
- const QString ms = m.join("\n");
- ui->pte_ModelDirectories->setPlaceholderText(ms);
- }
+ CSimulatorSettings m_generic = settings.getGenericSettings();
- const QStringList e = settings.getDefaultModelExcludeDirectoryPatterns();
- if (e.isEmpty()) { ui->pte_ExcludeDirectories->setPlaceholderText("Exclude directories"); }
+ // this setting is only visable for msfs2024 but we set it always to keep code simple. the checkbox is hidden in
+ // other simulators this settings are used to load/not load the modelset when starting swiftgui
+ ui->cb_WithDbEntry->setChecked(m_generic.getPropertyWithDbEntry());
+ ui->cb_DistributorFiltered->setChecked(m_generic.getPropertyDistributorFiltered());
+ ui->cb_LoadNewModelset->setChecked(m_generic.getPropertyModelSet());
+
+ // Storeable content should be displayed as planetext so that it is actually saved.
+ if (simulator.isMSFS2024())
+ {
+ QStringList m = m_generic.getModelDirectories();
+ if (m.isEmpty()) { ui->pte_ModelDirectories->setPlainText("*"); }
+ else
+ {
+ const QString ms = m.join("\n");
+ ui->pte_ModelDirectories->setPlainText(ms);
+ }
+
+ QStringList e = m_generic.getModelExcludeDirectoryPatterns();
+ if (e.isEmpty()) { ui->pte_ExcludeDirectories->setPlainText("PassiveAircraft\nSTUB\nZZZZ"); }
+ else
+ {
+ const QString es = e.join("\n");
+ ui->pte_ExcludeDirectories->setPlainText(es);
+ }
+ }
else
{
- const QString es = e.join("\n");
- ui->pte_ExcludeDirectories->setPlaceholderText(es);
+ const QStringList m = settings.getModelDirectoriesFromSimulatorDirectoryOrDefault();
+ if (m.isEmpty()) { ui->pte_ModelDirectories->setPlaceholderText("Model directories"); }
+ else
+ {
+ const QString ms = m.join("\n");
+ ui->pte_ModelDirectories->setPlainText(ms);
+ }
+
+ const QStringList e = settings.getDefaultModelExcludePatterns();
+ if (e.isEmpty()) { ui->pte_ExcludeDirectories->setPlaceholderText("Exclude directories"); }
+ else
+ {
+ const QString es = e.join("\n");
+ ui->pte_ExcludeDirectories->setPlainText(es);
+ }
}
}
diff --git a/src/gui/components/settingssimulatorbasicscomponent.ui b/src/gui/components/settingssimulatorbasicscomponent.ui
index a34d86d8b..08f847500 100644
--- a/src/gui/components/settingssimulatorbasicscomponent.ui
+++ b/src/gui/components/settingssimulatorbasicscomponent.ui
@@ -1,195 +1,268 @@
- CSettingsSimulatorBasicsComponent
-
-
-
- 0
- 0
- 459
- 347
-
-
-
- Simulator basic settings
-
-
-
- 3
-
-
- 3
-
-
- 3
-
-
- 3
-
- -
-
-
- Model directories
-
-
-
- -
-
-
- Simulator:
-
-
-
- -
-
-
- remove redundant directories, fix file paths ...
-
-
- adjust
-
-
-
- -
-
-
- Excluded from model loading
-
-
- Exclude directory patterns:
-
-
-
- -
-
-
-
- 150
- 25
-
-
-
-
- -
-
-
- Qt::Horizontal
-
-
- QSizePolicy::Minimum
-
-
-
- 50
- 20
-
-
-
-
- -
-
-
- save
-
-
-
- -
-
-
- reset
-
-
-
- -
-
-
- Simulator directory:
-
-
-
- -
-
-
- Model directories:
-
-
-
- -
-
-
- ...
-
-
-
- -
-
-
- ...
-
-
-
- -
-
-
- Excluded directory patterns
-
-
- QPlainTextEdit::NoWrap
-
-
- Excluded directory patterns
-
-
-
- -
-
-
- Simulator directory path
-
-
-
- -
-
-
- copy (materialize) defaults
-
-
- copy def.
-
-
-
- -
-
-
- ...
-
-
-
- -
-
-
- <html><head/><body><p><img src=":/diagona/icons/diagona/icons/exclamation--frame.png"/> Changing model directories means you have to update your model set! Check documentation on "creating a model set".</p></body></html>
-
-
- true
-
-
-
-
-
-
-
- swift::gui::components::CSimulatorSelector
- QFrame
- gui/components/simulatorselector.h
- 1
-
-
-
- le_SimulatorDirectory
- pte_ModelDirectories
- pte_ExcludeDirectories
-
-
-
+ CSettingsSimulatorBasicsComponent
+
+
+
+ 0
+ 0
+ 459
+ 347
+
+
+
+ Simulator basic settings
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ Model directories
+
+
+
+ -
+
+
+ Simulator:
+
+
+
+
+ -
+
+
+ remove redundant directories, fix file paths ...
+
+
+ adjust
+
+
+
+ -
+
+
+ Excluded from model loading
+
+
+ Exclude directory patterns:
+
+
+
+ -
+
+
+
+ 0
+ 100
+
+
+
+ Model set handling
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
-
+
+
+ true
+
+
+ Create new modelset when swiftgui starts?
+
+
+ Every time swiftgui is restarted, the models currently in the simulator are read in and a new model set is created
+
+
+
+ -
+
+
+ Load models only with database entries
+
+
+ Only models with swift database entry are used
+
+
+
+ -
+
+
+ Distributors filtered?
+
+
+ Only the distributors from the configuration file are used
+
+
+
+
+
+
+ -
+
+
+
+ 150
+ 25
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Minimum
+
+
+
+ 50
+ 20
+
+
+
+
+ -
+
+
+ save
+
+
+
+ -
+
+
+ reset
+
+
+ Sets the input mask to the default values and deletes the saved values.
+
+
+
+ -
+
+
+ Simulator directory:
+
+
+
+ -
+
+
+ Model directories:
+
+
+ remove redundant directories, fix file paths ...
+
+
+
+ -
+
+
+ ...
+
+
+
+ -
+
+
+ ...
+
+
+
+ -
+
+
+ Excluded directory patterns
+
+
+ QPlainTextEdit::NoWrap
+
+
+ Excluded directory patterns
+
+
+
+ -
+
+
+ Simulator directory path
+
+
+
+ -
+
+
+ copy (materialize) defaults
+
+
+ copy def.
+
+
+
+ -
+
+
+ ...
+
+
+
+ -
+
+
+ <html><head/><body><p><img src=":/diagona/icons/diagona/icons/exclamation--frame.png"/> Changing model directories means you have to update your model set! Check documentation on "creating a model set".</p></body></html>
+
+
+ true
+
+
+
+ 150
+ 100
+
+
+
+
+
+
+
+
+ swift::gui::components::CSimulatorSelector
+ QFrame
+ gui/components/simulatorselector.h
+ 1
+
+
+
+ le_SimulatorDirectory
+ pte_ModelDirectories
+ pte_ExcludeDirectories
+
+
+
diff --git a/src/gui/editors/ownmodelsetform.ui b/src/gui/editors/ownmodelsetform.ui
index c075c493d..99ecf31ca 100644
--- a/src/gui/editors/ownmodelsetform.ui
+++ b/src/gui/editors/ownmodelsetform.ui
@@ -164,6 +164,9 @@
all shown below
+
+ true
+
bg_Distributors
@@ -194,9 +197,6 @@
all distributors
-
- true
-
bg_Distributors
diff --git a/src/gui/models/aircraftmodellistmodel.cpp b/src/gui/models/aircraftmodellistmodel.cpp
index 9d6f69bab..564500703 100644
--- a/src/gui/models/aircraftmodellistmodel.cpp
+++ b/src/gui/models/aircraftmodellistmodel.cpp
@@ -36,6 +36,7 @@ namespace swift::gui::models
(void)QT_TRANSLATE_NOOP("CAircraftModelListModel", "model");
}
+ // TODO TZ check if some columns can be hidden automatically for different sims
void CAircraftModelListModel::setAircraftModelMode(CAircraftModelListModel::AircraftModelMode mode)
{
if (m_mode == mode) { return; }
@@ -45,7 +46,10 @@ namespace swift::gui::models
{
case NotSet:
case OwnAircraftModelClient:
- m_columns.addColumn(CColumn::standardString("model", { CAircraftModel::IndexModelString }));
+ // m_columns.addColumn(CColumn::standardString("model", { CAircraftModel::IndexModelString }));
+ m_columns.addColumn(CColumn::standardString("model", { CAircraftModel::IndexShortModelString }));
+
+ m_columns.addColumn(CColumn::standardString("liverypart", CAircraftModel::IndexModelLivery));
m_columns.addColumn(
CColumn("DB", "DB metadata", CAircraftModel::IndexDatabaseIcon, new CPixmapFormatter()));
m_columns.addColumn(
@@ -82,7 +86,9 @@ namespace swift::gui::models
case OwnAircraftModelMappingTool:
case StashModel:
- m_columns.addColumn(CColumn::standardString("model", CAircraftModel::IndexModelString));
+ m_columns.addColumn(CColumn::standardString("model", CAircraftModel::IndexShortModelString));
+
+ m_columns.addColumn(CColumn::standardString("liverypart", CAircraftModel::IndexModelLivery));
m_columns.addColumn(
CColumn("DB", "DB metadata", CAircraftModel::IndexDatabaseIcon, new CPixmapFormatter()));
if (mode == StashModel)
diff --git a/src/misc/CMakeLists.txt b/src/misc/CMakeLists.txt
index 40bd0d4c2..74c38efe9 100644
--- a/src/misc/CMakeLists.txt
+++ b/src/misc/CMakeLists.txt
@@ -533,6 +533,8 @@ add_library(misc SHARED
simulation/flightgear/aircraftmodelloaderflightgear.h
simulation/flightgear/flightgearutil.cpp
simulation/flightgear/flightgearutil.h
+ simulation/msfs2024/aircraftmodelloadermsfs2024.cpp
+ simulation/msfs2024/aircraftmodelloadermsfs2024.h
simulation/fscommon/aircraftcfgentries.cpp
simulation/fscommon/aircraftcfgentries.h
simulation/fscommon/aircraftcfgentrieslist.cpp
diff --git a/src/misc/simulation/aircraftmodel.cpp b/src/misc/simulation/aircraftmodel.cpp
index f63aa00c0..cc69cf773 100644
--- a/src/misc/simulation/aircraftmodel.cpp
+++ b/src/misc/simulation/aircraftmodel.cpp
@@ -42,6 +42,10 @@ namespace swift::misc::simulation
: m_modelString(model.trimmed().toUpper()), m_modelType(type)
{}
+ CAircraftModel::CAircraftModel(const QString &model, const QString &livery, CAircraftModel::ModelType type)
+ : m_modelString(model.trimmed().toUpper()), m_modelLivery(livery.trimmed().toUpper()), m_modelType(type)
+ {}
+
CAircraftModel::CAircraftModel(const QString &model, CAircraftModel::ModelType type, const CAircraftIcaoCode &icao,
const CLivery &livery)
: m_aircraftIcao(icao), m_livery(livery), m_modelString(model.trimmed().toUpper()), m_modelType(type)
@@ -237,6 +241,12 @@ namespace swift::misc::simulation
return this->getAllModelStringsAndAliases() % " " % this->getDbKeyAsStringInParentheses();
}
+ QString CAircraftModel::getMsfs2024Modelstring()
+ {
+ m_modelString = m_modelString.trimmed().toUpper() % u" " % m_modelLivery.trimmed().toUpper();
+ return m_modelString;
+ }
+
bool CAircraftModel::isVtol() const { return this->getAircraftIcaoCode().isVtol(); }
QVariant CAircraftModel::propertyByIndex(swift::misc::CPropertyIndexRef index) const
@@ -274,6 +284,8 @@ namespace swift::misc::simulation
case IndexLivery: return m_livery.propertyByIndex(index.copyFrontRemoved());
case IndexCallsign: return m_callsign.propertyByIndex(index.copyFrontRemoved());
case IndexMembersDbStatus: return this->getMembersDbStatus();
+ case IndexModelLivery: return QVariant(m_modelLivery);
+ case IndexShortModelString: return QVariant(getShortModelString());
default: return CValueObject::propertyByIndex(index);
}
}
@@ -584,6 +596,23 @@ namespace swift::misc::simulation
return (sim.isFG()) ? this->getSwiftLiveryString(true, false, false) : this->getSwiftLiveryString();
}
+ QString CAircraftModel::getShortModelString() const
+ {
+
+ QString shortModelString = m_modelString;
+ if (m_modelString.contains(m_modelLivery))
+ {
+ int lastIndex = m_modelString.lastIndexOf(m_modelLivery);
+
+ if (lastIndex != -1)
+ {
+ const QString newModelString = m_modelString.left(lastIndex);
+ shortModelString = newModelString;
+ }
+ }
+ return shortModelString;
+ }
+
DBTripleIds CAircraftModel::parseNetworkLiveryString(const QString &liveryString)
{
// "swift_m22l33a11"
@@ -766,6 +795,13 @@ namespace swift::misc::simulation
return (p.contains(path, cs));
}
+ bool CAircraftModel::matchesModelStringAndLivery(const QString &modelString, const QString &modelLivery,
+ Qt::CaseSensitivity sensitivity) const
+ {
+ return (stringCompare(modelString, m_modelString, sensitivity) &&
+ stringCompare(modelLivery, m_modelLivery, sensitivity));
+ }
+
bool CAircraftModel::matchesModelString(const QString &modelString, Qt::CaseSensitivity sensitivity) const
{
return stringCompare(modelString, m_modelString, sensitivity);
@@ -999,6 +1035,7 @@ namespace swift::misc::simulation
const QString modelName(json.value(prefix % u"name").toString());
const QString modelMode(json.value(prefix % u"mode").toString());
const QString parts(json.value(prefix % u"parts").toString());
+ const QString modelLivery(json.value(prefix % u"modellivery").toString());
// check for undefined to rule out 0ft values
const QJsonValue cgjv = json.value(prefix % u"cgft");
@@ -1008,6 +1045,7 @@ namespace swift::misc::simulation
const CSimulatorInfo simInfo = CSimulatorInfo::fromDatabaseJson(json, prefix);
CAircraftModel model(modelString, CAircraftModel::TypeDatabaseEntry, simInfo, modelName, modelDescription);
model.setModelStringAlias(modelStringAlias);
+ model.setModelLivery(modelLivery); // msfs2024
model.setModelModeAsString(modelMode);
model.setSupportedParts(parts);
model.setCG(cg);
diff --git a/src/misc/simulation/aircraftmodel.h b/src/misc/simulation/aircraftmodel.h
index 2cc21462c..04066c886 100644
--- a/src/misc/simulation/aircraftmodel.h
+++ b/src/misc/simulation/aircraftmodel.h
@@ -83,7 +83,9 @@ namespace swift::misc
TypeManuallySet, //!< manually set, e.g. from GUI
TypeOwnSimulatorModel, //!< represents own simulator model (AI model, model on disk)
TypeVPilotRuleBased, //!< based on a vPilot rule
- TypeTerrainProbe //!< peudo aircraft used for terrain probing (FSX)
+ TypeTerrainProbe, //!< peudo aircraft used for terrain probing (FSX)
+ TypeOwnSimulatorLivery //!< represents own simulator model livery (msfs2024)
+
};
//! Mode, decides if a model is supposed to be used in the model set for model matching
@@ -124,7 +126,9 @@ namespace swift::misc
IndexSupportedParts,
IndexModelModeAsIcon,
IndexHasQueriedModelString,
- IndexMembersDbStatus
+ IndexMembersDbStatus,
+ IndexModelLivery, // MSFS2024
+ IndexShortModelString,
};
//! \copydoc swift::misc::CValueObject::registerMetadata
@@ -136,6 +140,9 @@ namespace swift::misc
//! Constructor.
CAircraftModel(const QString &model, ModelType type);
+ //! Constructor.
+ CAircraftModel(const QString &model, const QString &livery, ModelType type);
+
//! Constructor.
CAircraftModel(const QString &model, ModelType type, const aviation::CAircraftIcaoCode &icao,
const aviation::CLivery &livery);
@@ -181,12 +188,21 @@ namespace swift::misc
//! Model key, either queried or loaded from simulator model
const QString &getModelString() const { return m_modelString; }
+ //! Model Livery, part of model string in MSFS 2024
+ const QString &getModelLivery() const { return m_modelLivery; }
+
//! Model string and DB key (if available)
QString getModelStringAndDbKey() const;
//! Model string
void setModelString(const QString &modelString) { m_modelString = modelString.trimmed().toUpper(); }
+ //! Model livery msfs2024
+ void setModelLivery(const QString &modelLivery) { m_modelLivery = modelLivery.trimmed().toUpper(); }
+
+ //! Model livery whitout part for lifery msfs2024
+ QString getShortModelString() const;
+
//! Model key, either queried or loaded from simulator model
const QString &getModelStringAlias() const { return m_modelStringAlias; }
@@ -196,6 +212,9 @@ namespace swift::misc
//! Get model string and aliases
QString getAllModelStringsAliasesAndDbKey() const;
+ //! Get model string and Livery
+ QString getMsfs2024Modelstring();
+
//! Model string alias
void setModelStringAlias(const QString &alias) { m_modelStringAlias = alias.trimmed().toUpper(); }
@@ -419,6 +438,10 @@ namespace swift::misc
//! Matches model string?
bool matchesModelString(const QString &modelString, Qt::CaseSensitivity sensitivity) const;
+ //! Matches model string and livery?
+ bool matchesModelStringAndLivery(const QString &modelString, const QString &modelLivery,
+ Qt::CaseSensitivity sensitivity) const;
+
//! Matches model string or alias?
bool matchesModelStringOrAlias(const QString &modelString, Qt::CaseSensitivity sensitivity) const;
@@ -567,11 +590,13 @@ namespace swift::misc
CSimulatorInfo m_simulator; //!< model for given simulator
CDistributor m_distributor; //!< who designed or distributed the model
QString m_modelString; //!< Simulator model key, unique
+ QString m_modelLivery; //!< Simulator livery (msfs2024)
QString m_modelStringAlias; //!< Simulator model key alias, unique
QString m_name; //!< Model name
QString m_description; //!< descriptive text
QString m_fileName; //!< file name
QString m_supportedParts; //!< supported parts
+ QString m_shortModelString; //!< cached short model string
qint64 m_fileTimestamp = -1; //!< file timestamp of originating file (if applicable)
ModelType m_modelType = TypeUnknown; //!< model string is coming representing ...?
ModelMode m_modelMode = Include; //!< model mode (include / exclude)
@@ -591,6 +616,7 @@ namespace swift::misc
SWIFT_METAMEMBER(supportedParts),
SWIFT_METAMEMBER(modelString, 0, CaseInsensitiveComparison),
SWIFT_METAMEMBER(modelStringAlias, 0, CaseInsensitiveComparison),
+ SWIFT_METAMEMBER(modelLivery, 0, CaseInsensitiveComparison),
SWIFT_METAMEMBER(name),
SWIFT_METAMEMBER(description, 0, DisabledForComparison),
SWIFT_METAMEMBER(fileName, 0, DisabledForComparison),
diff --git a/src/misc/simulation/aircraftmodellist.cpp b/src/misc/simulation/aircraftmodellist.cpp
index 309e86526..c994a2803 100644
--- a/src/misc/simulation/aircraftmodellist.cpp
+++ b/src/misc/simulation/aircraftmodellist.cpp
@@ -731,12 +731,14 @@ namespace swift::misc::simulation
return d;
}
- bool CAircraftModelList::removeModelWithString(const QString &modelString, Qt::CaseSensitivity sensitivity)
+ bool CAircraftModelList::removeModelWithString(const QString &modelString, const QString &modelLivery,
+ Qt::CaseSensitivity sensitivity)
{
if (modelString.isEmpty()) { return false; }
if (this->isEmpty()) { return false; }
- const int r = this->removeIf(
- [&](const CAircraftModel &model) { return model.matchesModelString(modelString, sensitivity); });
+ const int r = this->removeIf([&](const CAircraftModel &model) {
+ return model.matchesModelStringAndLivery(modelString, modelLivery, sensitivity);
+ });
return r > 0;
}
@@ -849,7 +851,11 @@ namespace swift::misc::simulation
Qt::CaseSensitivity sensitivity)
{
bool r = false;
- if (!this->isEmpty()) { r = this->removeModelWithString(addOrReplaceModel.getModelString(), sensitivity); }
+ if (!this->isEmpty())
+ {
+ r = this->removeModelWithString(addOrReplaceModel.getModelString(), addOrReplaceModel.getModelLivery(),
+ sensitivity);
+ }
this->push_back(addOrReplaceModel);
return r;
}
@@ -907,6 +913,18 @@ namespace swift::misc::simulation
return ms;
}
+ QStringList CAircraftModelList::getModelStringAndLiveryList(bool sort) const
+ {
+ QStringList ms;
+ for (const CAircraftModel &model : *this)
+ {
+ if (!model.hasModelString()) { continue; }
+ ms.append(model.getModelString() + "{" + model.getModelLivery() + "}");
+ }
+ if (sort) { ms.sort(Qt::CaseInsensitive); }
+ return ms;
+ }
+
QSet CAircraftModelList::getModelStringSet() const
{
CSetBuilder ms;
@@ -1731,12 +1749,12 @@ namespace swift::misc::simulation
if (valid)
{
validModels.push_back(model);
- invalidModels.removeModelWithString(model.getModelString(), Qt::CaseInsensitive);
+ invalidModels.removeModelWithString(model.getModelString(), model.getModelLivery(), Qt::CaseInsensitive);
}
else
{
invalidModels.push_back(model);
- validModels.removeModelWithString(model.getModelString(), Qt::CaseInsensitive);
+ validModels.removeModelWithString(model.getModelString(), model.getModelLivery(), Qt::CaseInsensitive);
}
}
diff --git a/src/misc/simulation/aircraftmodellist.h b/src/misc/simulation/aircraftmodellist.h
index 2fd3c2127..0c595543a 100644
--- a/src/misc/simulation/aircraftmodellist.h
+++ b/src/misc/simulation/aircraftmodellist.h
@@ -334,7 +334,8 @@ namespace swift::misc
//! Remove those models with given model strings
//! \return number of elements removed
- bool removeModelWithString(const QString &modelString, Qt::CaseSensitivity sensitivity);
+ bool removeModelWithString(const QString &modelString, const QString &modelLivery,
+ Qt::CaseSensitivity sensitivity);
//! Remove those models with given model strings
//! \return number of elements removed
@@ -393,6 +394,9 @@ namespace swift::misc
//! Model strings
QStringList getModelStringList(bool sort = true) const;
+ //! Model strings and Livery codes
+ QStringList getModelStringAndLiveryList(bool sort = true) const;
+
//! Model strings as set
QSet getModelStringSet() const;
diff --git a/src/misc/simulation/aircraftmodelloader.cpp b/src/misc/simulation/aircraftmodelloader.cpp
index ce08841fe..a5a11fb8b 100644
--- a/src/misc/simulation/aircraftmodelloader.cpp
+++ b/src/misc/simulation/aircraftmodelloader.cpp
@@ -131,18 +131,28 @@ namespace swift::misc::simulation
return;
}
- // really load from disk?
- const QStringList modelDirs = this->getInitializedModelDirectories(modelDirectories, simulator);
- if (m_skipLoadingEmptyModelDir && modelDirs.isEmpty())
+ QStringList modelDirs = { "", "" };
+ if (simulator.isMSFS2024())
{
- const CStatusMessage status = CStatusMessage(this, CStatusMessage::SeverityWarning,
- u"Empty or not existing '%1' directory '%2', skipping read")
- << simulator.toQString() << modelDirectories.join(", ");
- m_loadingMessages.push_back(status);
- m_loadingMessages.freezeOrder();
emit this->loadingFinished(m_loadingMessages, simulator, LoadingSkipped);
return;
}
+ else
+ {
+ // really load from disk?
+ modelDirs = this->getInitializedModelDirectories(modelDirectories, simulator);
+ if (m_skipLoadingEmptyModelDir && modelDirs.isEmpty())
+ {
+ const CStatusMessage status =
+ CStatusMessage(this, CStatusMessage::SeverityWarning,
+ u"Empty or not existing '%1' directory '%2', skipping read")
+ << simulator.toQString() << modelDirectories.join(", ");
+ m_loadingMessages.push_back(status);
+ m_loadingMessages.freezeOrder();
+ emit this->loadingFinished(m_loadingMessages, simulator, LoadingSkipped);
+ return;
+ }
+ }
this->setObjectInfo(simulator);
this->startLoadingFromDisk(mode, modelConsolidation, modelDirs);
diff --git a/src/misc/simulation/aircraftmodelloaderprovider.cpp b/src/misc/simulation/aircraftmodelloaderprovider.cpp
index f81c7e196..ebf5c169e 100644
--- a/src/misc/simulation/aircraftmodelloaderprovider.cpp
+++ b/src/misc/simulation/aircraftmodelloaderprovider.cpp
@@ -13,6 +13,7 @@
#include "misc/mixin/mixincompare.h"
#include "misc/simulation/flightgear/aircraftmodelloaderflightgear.h"
#include "misc/simulation/fscommon/aircraftcfgparser.h"
+#include "misc/simulation/msfs2024/aircraftmodelloadermsfs2024.h"
#include "misc/simulation/xplane/aircraftmodelloaderxplane.h"
#include "misc/simulation/xplane/xplaneutil.h"
@@ -22,6 +23,7 @@ using namespace swift::misc::simulation::settings;
using namespace swift::misc::simulation::fscommon;
using namespace swift::misc::simulation::flightgear;
using namespace swift::misc::simulation::xplane;
+using namespace swift::misc::simulation::msfs2024;
namespace swift::misc::simulation
{
@@ -31,6 +33,7 @@ namespace swift::misc::simulation
Q_ASSERT_X(simulator.isSingleSimulator(), Q_FUNC_INFO, "Single simulator");
if (simulator.isXPlane()) { return new CAircraftModelLoaderXPlane(parent); }
if (simulator.isFG()) { return new CAircraftModelLoaderFlightgear(parent); }
+ if (simulator.isMSFS2024()) { return new CAircraftModelLoaderMsfs2024(parent); }
return CAircraftCfgParser::createModelLoader(simulator, parent);
}
diff --git a/src/misc/simulation/aircraftmodelutils.cpp b/src/misc/simulation/aircraftmodelutils.cpp
index 7682dc04e..be8a908b0 100644
--- a/src/misc/simulation/aircraftmodelutils.cpp
+++ b/src/misc/simulation/aircraftmodelutils.cpp
@@ -167,9 +167,13 @@ namespace swift::misc::simulation
QStringLiteral("Some of the excluded models are: '%1'").arg(ms), true));
}
- // specific checks for FSX/XPlane/FG
+ // specific checks for FSX/XPlane/FG/MSFS
CStatusMessageList specificTests;
- if (simulator.isMicrosoftOrPrepare3DSimulator() || models.isLikelyFsFamilyModelList())
+ if (simulator.isMSFS2024())
+ {
+ // Placeholder, nothing to do
+ }
+ else if (simulator.isMicrosoftOrPrepare3DSimulator() || models.isLikelyFsFamilyModelList())
{
const CStatusMessageList specificTests1 = fscommon::CFsCommonUtil::validateAircraftConfigFiles(
models, validModels, invalidModels, ignoreEmpty, stopAtFailedFiles, wasStopped);
@@ -193,12 +197,6 @@ namespace swift::misc::simulation
models, validModels, invalidModels, ignoreEmpty, stopAtFailedFiles, wasStopped, simulatorDir);
specificTests.push_back(specificTests2);
}
- else if (simulator.isMSFS2024())
- {
- const CStatusMessageList specificTests2 = fscommon::CFsCommonUtil::validateMSFS2024SimObjectsPath(
- models, validModels, invalidModels, ignoreEmpty, stopAtFailedFiles, wasStopped, simulatorDir);
- specificTests.push_back(specificTests2);
- }
}
else if (simulator.isXPlane() || models.isLikelyXPlaneModelList())
{
diff --git a/src/misc/simulation/fscommon/aircraftcfgparser.cpp b/src/misc/simulation/fscommon/aircraftcfgparser.cpp
index f9bf5448c..8b7a52b46 100644
--- a/src/misc/simulation/fscommon/aircraftcfgparser.cpp
+++ b/src/misc/simulation/fscommon/aircraftcfgparser.cpp
@@ -153,11 +153,9 @@ namespace swift::misc::simulation::fscommon
// set directory with name filters, get aircraft.cfg and sub directories
static const QString NoNameFilter;
QDir dir(directory, NoNameFilter, QDir::Name, QDir::Files | QDir::AllDirs | QDir::NoDotAndDotDot);
- // TODO TZ: still have to figure out how msfs2024 handles this
+
// for MSFS2020 we only need aircraft.cfg
- // MSFS2024 has aircraft.cfg only in communityfolder
- // a solution for the aircraft from the marketplace may be prepared by ASOBO
- dir.setNameFilters(fileNameFilters(getSimulator().isMSFS(), getSimulator().isMSFS2024()));
+ dir.setNameFilters(fileNameFilters(getSimulator().isMSFS()));
if (!dir.exists())
{
return CAircraftCfgEntriesList(); // can happen if there are shortcuts or linked dirs not available
@@ -467,10 +465,9 @@ namespace swift::misc::simulation::fscommon
return content;
}
- // TODO TZ: MSFS2024 currently has aircraft.cfg only in the community folder
- const QStringList &CAircraftCfgParser::fileNameFilters(bool isMSFS, bool isMSFS2024)
+ const QStringList &CAircraftCfgParser::fileNameFilters(bool isMSFS)
{
- if (CBuildConfig::buildWordSize() == 32 || isMSFS || isMSFS2024)
+ if (CBuildConfig::buildWordSize() == 32 || isMSFS)
{
static const QStringList f({ "aircraft.cfg" });
return f;
diff --git a/src/misc/simulation/fscommon/aircraftcfgparser.h b/src/misc/simulation/fscommon/aircraftcfgparser.h
index 4fdc9bff5..1af923bd4 100644
--- a/src/misc/simulation/fscommon/aircraftcfgparser.h
+++ b/src/misc/simulation/fscommon/aircraftcfgparser.h
@@ -89,7 +89,7 @@ namespace swift::misc
static QString getFixedIniLineContent(const QString &line);
//! Files to be used
- static const QStringList &fileNameFilters(bool isMSFS, bool isMSFS2024);
+ static const QStringList &fileNameFilters(bool isMSFS);
//! Exclude the sub directories not to be parsed
static bool isExcludedSubDirectory(const QString &excludeDirectory);
diff --git a/src/misc/simulation/fscommon/fscommonutil.cpp b/src/misc/simulation/fscommon/fscommonutil.cpp
index e8c8d1e0d..bd14ae5eb 100644
--- a/src/misc/simulation/fscommon/fscommonutil.cpp
+++ b/src/misc/simulation/fscommon/fscommonutil.cpp
@@ -230,17 +230,6 @@ namespace swift::misc::simulation::fscommon
stopAtFailedFiles, stopped);
}
- CStatusMessageList CFsCommonUtil::validateMSFS2024SimObjectsPath(
- const CAircraftModelList &models, CAircraftModelList &validModels, CAircraftModelList &invalidModels,
- bool ignoreEmptyFileNames, int stopAtFailedFiles, std::atomic_bool &stopped, const QString &simulatorDir)
- {
- Q_UNUSED(simulatorDir)
- const QStringList simObjectPaths = CFsDirectories::msfs2024SimObjectsDirPath();
- return CFsCommonUtil::validateSimObjectsPath(QSet(simObjectPaths.begin(), simObjectPaths.end()),
- models, validModels, invalidModels, ignoreEmptyFileNames,
- stopAtFailedFiles, stopped);
- }
-
CStatusMessageList
CFsCommonUtil::validateSimObjectsPath(const QSet &simObjectDirs, const CAircraftModelList &models,
CAircraftModelList &validModels, CAircraftModelList &invalidModels,
diff --git a/src/misc/simulation/fscommon/fscommonutil.h b/src/misc/simulation/fscommon/fscommonutil.h
index 07bfa55fc..7cde5deb1 100644
--- a/src/misc/simulation/fscommon/fscommonutil.h
+++ b/src/misc/simulation/fscommon/fscommonutil.h
@@ -67,15 +67,6 @@ namespace swift::misc::simulation::fscommon
bool ignoreEmptyFileNames, int stopAtFailedFiles,
std::atomic_bool &wasStopped, const QString &simulatorDir);
- //! Validate if known SimObjects path are used
- //! \remark only for MSFS2024
- static CStatusMessageList validateMSFS2024SimObjectsPath(const CAircraftModelList &models,
- CAircraftModelList &validModels,
- CAircraftModelList &invalidModels,
- bool ignoreEmptyFileNames, int stopAtFailedFiles,
- std::atomic_bool &wasStopped,
- const QString &simulatorDir);
-
private:
//! Validate if known SimObjects path are used
//! \remark only for P3D/FSX
diff --git a/src/misc/simulation/fscommon/fsdirectories.cpp b/src/misc/simulation/fscommon/fsdirectories.cpp
index 020e32d7d..c2e883d23 100644
--- a/src/misc/simulation/fscommon/fsdirectories.cpp
+++ b/src/misc/simulation/fscommon/fsdirectories.cpp
@@ -331,6 +331,8 @@ namespace swift::misc::simulation::fscommon
"OneStore/microsoft-discovery",
"landingchallenge",
"tutorials",
+ "point-of-interest",
+ "airport",
};
return exclude;
@@ -338,9 +340,7 @@ namespace swift::misc::simulation::fscommon
const QStringList &CFsDirectories::msfs2024SimObjectsExcludeDirectoryPatterns()
{
- static const QStringList exclude {
- "landingchallenge",
- "tutorials",
+ static const QStringList exclude { "PassiveAircraft", "STUB", "ZZZZ"
};
return exclude;
diff --git a/src/misc/simulation/msfs2024/aircraftmodelloadermsfs2024.cpp b/src/misc/simulation/msfs2024/aircraftmodelloadermsfs2024.cpp
new file mode 100644
index 000000000..07ee7fc60
--- /dev/null
+++ b/src/misc/simulation/msfs2024/aircraftmodelloadermsfs2024.cpp
@@ -0,0 +1,107 @@
+// SPDX-FileCopyrightText: Copyright (C) 2019 swift Project Community / Contributors
+// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
+
+// #include
+
+#include "misc/simulation/msfs2024/aircraftmodelloadermsfs2024.h"
+
+#include
+
+#include "misc/simulation/aircraftmodel.h"
+#include "misc/simulation/settings/simulatorsettings.h"
+
+using namespace swift::config;
+using namespace swift::misc;
+using namespace swift::misc::aviation;
+using namespace swift::misc::physical_quantities;
+using namespace swift::misc::geo;
+using namespace swift::misc::network;
+using namespace swift::misc::simulation;
+using namespace swift::misc::simulation::settings;
+// using namespace swift::misc::math;
+using namespace swift::misc::simulation::data;
+// using namespace swift::misc::simulation::msfs2024;
+// using namespace swift::misc::simulation::settings;
+
+namespace swift::misc::simulation::msfs2024
+{
+
+ bool CAircraftModelLoaderMsfs2024::isLoadingFinished() const
+ {
+ return !m_parserWorker || m_parserWorker->isFinished();
+ ;
+ }
+
+ CAircraftModelLoaderMsfs2024::CAircraftModelLoaderMsfs2024(QObject *parent)
+ : simulation::IAircraftModelLoader(simulation::CSimulatorInfo::msfs2024(), parent)
+ {}
+
+ CAircraftModelLoaderMsfs2024::~CAircraftModelLoaderMsfs2024()
+ {
+ // that should be safe as long as the worker uses deleteLater (which it does)
+ if (m_parserWorker) { m_parserWorker->waitForFinished(); }
+ }
+
+ void CAircraftModelLoaderMsfs2024::updateInstalledModels(const CAircraftModelList &models)
+ {
+ this->setModelsForSimulator(models, CSimulatorInfo::msfs2024());
+
+ const CStatusMessage m =
+ CStatusMessage(this, CStatusMessage::SeverityInfo, u"MSFS2024 found and updated '%1' models")
+ << models.size();
+ m_loadingMessages.push_back(m);
+ }
+
+ CAircraftModelList CAircraftModelLoaderMsfs2024::performParsing()
+ {
+ CAircraftModelList allModels;
+
+ // TODO TZ Implement model queries via SimConnect if possible
+ // misc shut not include simconnect headers or plugins directly
+
+ const CSimulatorInfo simulatorInfo = CSimulatorInfo::msfs2024();
+ allModels =
+ CCentralMultiSimulatorModelCachesProvider::modelCachesInstance().getSynchronizedCachedModels(simulatorInfo);
+
+ return allModels;
+ }
+
+ // for msfs2024, the model information is read from the SimConnect interface and no longer from the directories via
+ // the aircraft.cfg
+ void CAircraftModelLoaderMsfs2024::startLoadingFromDisk(
+ IAircraftModelLoader::LoadMode mode, const IAircraftModelLoader::ModelConsolidationCallback &modelConsolidation,
+ const QStringList &modelDirectories)
+ {
+
+ const CSimulatorInfo simulator = CSimulatorInfo::msfs2024();
+ const QStringList modelDirs = this->getInitializedModelDirectories(modelDirectories, simulator);
+ const QStringList excludedDirectoryPatterns(
+ m_settings.getModelExcludeDirectoryPatternsOrDefault(simulator)); // copy
+
+ if (mode.testFlag(LoadInBackground))
+ {
+ if (m_parserWorker && !m_parserWorker->isFinished()) { return; }
+ emit this->diskLoadingStarted(simulator, mode);
+
+ // TODO TZ simplify, we don't need directories
+ m_parserWorker = CWorker::fromTask(this, "CAircraftModelLoaderMsfs2024::performParsing",
+ [this, modelDirs, excludedDirectoryPatterns, modelConsolidation]() {
+ auto models = this->performParsing();
+ if (modelConsolidation) { modelConsolidation(models, true); }
+ return models;
+ });
+ m_parserWorker->thenWithResult(this, [=](const auto &models) {
+ this->updateInstalledModels(models);
+ m_loadingMessages.freezeOrder();
+ emit this->loadingFinished(m_loadingMessages, simulator, ParsedData);
+ });
+ }
+ else if (mode.testFlag(LoadDirectly))
+ {
+ emit this->diskLoadingStarted(simulator, mode);
+ CAircraftModelList models(this->performParsing());
+ this->updateInstalledModels(models);
+ }
+ }
+
+} // namespace swift::misc::simulation::msfs2024
diff --git a/src/misc/simulation/msfs2024/aircraftmodelloadermsfs2024.h b/src/misc/simulation/msfs2024/aircraftmodelloadermsfs2024.h
new file mode 100644
index 000000000..a7dd37cc0
--- /dev/null
+++ b/src/misc/simulation/msfs2024/aircraftmodelloadermsfs2024.h
@@ -0,0 +1,44 @@
+// SPDX-FileCopyrightText: Copyright (C) 2019 swift Project Community / Contributors
+// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
+
+#ifndef SWIFT_MISC_SIMULATION_MSFS2024_AIRCRAFTMODELLOADERMSFS2024_H
+#define SWIFT_MISC_SIMULATION_MSFS2024_AIRCRAFTMODELLOADERMSFS2024_H
+
+#include
+
+#include "misc/simulation/aircraftmodelloader.h"
+
+namespace swift::misc::simulation::msfs2024
+{
+ //! Msfs2024 aircraft model loader
+ class CAircraftModelLoaderMsfs2024 : public IAircraftModelLoader
+ {
+ Q_OBJECT
+
+ public:
+ //! Constructor
+ CAircraftModelLoaderMsfs2024(QObject *parent = nullptr);
+
+ //! Virtual destructor
+ ~CAircraftModelLoaderMsfs2024() override;
+
+ //! Parsed or injected models
+ void updateInstalledModels(const CAircraftModelList &models);
+
+ //! \copydoc IAircraftModelLoader::isLoadingFinished
+ bool isLoadingFinished() const override;
+
+ protected:
+ //! \copydoc IAircraftModelLoader::startLoadingFromDisk
+ void startLoadingFromDisk(LoadMode mode, const ModelConsolidationCallback &modelConsolidation,
+ const QStringList &modelDirectories) override;
+
+ private:
+ QPointer m_parserWorker;
+ // CAircraftModelList performParsing(const QStringList &rootDirectories, const QStringList &excludeDirectories);
+ CAircraftModelList performParsing();
+ };
+
+} // namespace swift::misc::simulation::msfs2024
+
+#endif // SWIFT_MISC_SIMULATION_MSFS2024_AIRCRAFTMODELLOADERMSFS2024_H
diff --git a/src/misc/simulation/settings/simulatorsettings.cpp b/src/misc/simulation/settings/simulatorsettings.cpp
index 896d80d5a..cc5581676 100644
--- a/src/misc/simulation/settings/simulatorsettings.cpp
+++ b/src/misc/simulation/settings/simulatorsettings.cpp
@@ -154,6 +154,16 @@ namespace swift::misc::simulation::settings
}
}
+ void CSimulatorSettings::setPropertyModelSet(bool value) { value ? m_modelSet = true : m_modelSet = false; }
+ void CSimulatorSettings::setPropertyWithDbEntry(bool value)
+ {
+ value ? m_withDbEntry = true : m_withDbEntry = false;
+ }
+ void CSimulatorSettings::setPropertyDistributorFiltered(bool value)
+ {
+ value ? m_distributorFiltered = true : m_distributorFiltered = false;
+ }
+
void CSimulatorSettings::setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant)
{
if (index.isMyself())
@@ -619,7 +629,7 @@ namespace swift::misc::simulation::settings
return m_genericSettings.getModelDirectories();
}
- const QStringList &CSpecializedSimulatorSettings::getDefaultModelExcludeDirectoryPatterns() const
+ const QStringList &CSpecializedSimulatorSettings::getDefaultModelExcludePatterns() const
{
return CSpecializedSimulatorSettings::defaultModelExcludeDirectoryPatterns(m_simulator);
}
@@ -694,8 +704,9 @@ namespace swift::misc::simulation::settings
}
case CSimulatorInfo::MSFS2024:
{
- static const QString msfs2024 =
- CFileUtils::normalizeFilePathToQtStandard(CFsDirectories::msfs2024PackagesDir());
+ // msfs2024 uses no model directories but uses the field "packages directory" for filtering modelstrings
+ // Asterix stands for everything == no filtering
+ static const QString msfs2024 = "*";
if (msfs2024.isEmpty()) { return e; }
static const QStringList md { msfs2024 };
return md;
diff --git a/src/misc/simulation/settings/simulatorsettings.h b/src/misc/simulation/settings/simulatorsettings.h
index c683177b3..4aec82c37 100644
--- a/src/misc/simulation/settings/simulatorsettings.h
+++ b/src/misc/simulation/settings/simulatorsettings.h
@@ -120,6 +120,11 @@ namespace swift::misc::simulation::settings
//! Record GND values with radius
bool setRecordedGndRadius(const swift::misc::physical_quantities::CLength &radius);
+ //! Reads the settings for automatic loading when starting swiftgui
+ bool getPropertyWithDbEntry() { return m_withDbEntry; }
+ bool getPropertyModelSet() { return m_modelSet; }
+ bool getPropertyDistributorFiltered() { return m_distributorFiltered; }
+
//! Reset the paths
void resetPaths();
@@ -135,6 +140,10 @@ namespace swift::misc::simulation::settings
//! \copydoc swift::misc::mixin::Index::setPropertyByIndex
void setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant);
+ void setPropertyModelSet(bool value);
+ void setPropertyWithDbEntry(bool value);
+ void setPropertyDistributorFiltered(bool value);
+
private:
QString m_simulatorDirectory; //!< Simulator directory
QStringList m_modelDirectories; //!< Model directory
@@ -142,6 +151,11 @@ namespace swift::misc::simulation::settings
bool m_comIntegration = false; //!< COM integration
bool m_recordGnd = false; //!< Record GND values (of own aircraft)
int m_cgSource = static_cast(CGFromSimulatorFirst); //!< CG source
+ bool m_modelSet = false; //!< Reads models from simulator via SimConnect (msfs2024 only)
+ bool m_withDbEntry = false; //!< Only models with swift database entry are used after reading (msfs2024 only)
+ bool m_distributorFiltered =
+ false; //!< Only the distributors from the configuration file are used (msfs2024 only)
+
physical_quantities::CLength m_recordedGndRadius { 250.0, physical_quantities::CLengthUnit::m() };
SWIFT_METACLASS(
@@ -152,7 +166,10 @@ namespace swift::misc::simulation::settings
SWIFT_METAMEMBER(comIntegration),
SWIFT_METAMEMBER(cgSource),
SWIFT_METAMEMBER(recordGnd),
- SWIFT_METAMEMBER(recordedGndRadius));
+ SWIFT_METAMEMBER(recordedGndRadius),
+ SWIFT_METAMEMBER(modelSet),
+ SWIFT_METAMEMBER(withDbEntry),
+ SWIFT_METAMEMBER(distributorFiltered));
};
//! Some P3D/FSX settings
@@ -240,7 +257,7 @@ namespace swift::misc::simulation::settings
const QStringList &getModelDirectoriesIfNotDefault() const;
//! Default model exclude patterns
- const QStringList &getDefaultModelExcludeDirectoryPatterns() const;
+ const QStringList &getDefaultModelExcludePatterns() const;
//! First model directoy
QString getFirstModelDirectoryOrDefault() const;
diff --git a/src/misc/simulation/simulatedaircraft.h b/src/misc/simulation/simulatedaircraft.h
index 7d8ffcd47..5dc31f20c 100644
--- a/src/misc/simulation/simulatedaircraft.h
+++ b/src/misc/simulation/simulatedaircraft.h
@@ -409,6 +409,12 @@ namespace swift::misc
//! Get model string
const QString &getModelString() const { return m_models[CurrentModel].getModelString(); }
+ //! Get model Livery MSFS2024
+ const QString &getLiveryString() const { return m_models[CurrentModel].getModelLivery(); }
+
+ //! Get short model string (without livery)
+ const QString getShortModelString() const { return m_models[CurrentModel].getShortModelString(); }
+
//! Set model string
void setModelString(const QString &modelString);
diff --git a/src/plugins/simulator/CMakeLists.txt b/src/plugins/simulator/CMakeLists.txt
index 4572f5131..17c1c4039 100644
--- a/src/plugins/simulator/CMakeLists.txt
+++ b/src/plugins/simulator/CMakeLists.txt
@@ -22,7 +22,7 @@ if(SWIFT_BUILD_FS9_PLUGIN OR SWIFT_BUILD_FSX_PLUGIN OR SWIFT_BUILD_P3D_PLUGIN OR
add_subdirectory(fscommon)
endif()
-if(SWIFT_BUILD_FSX_PLUGIN OR SWIFT_BUILD_P3D_PLUGIN OR SWIFT_BUILD_MSFS_PLUGIN OR SWIFT_BUILD_MSFS2024_PLUGIN)
+if(SWIFT_BUILD_FSX_PLUGIN OR SWIFT_BUILD_P3D_PLUGIN OR SWIFT_BUILD_MSFS_PLUGIN)
add_subdirectory(fsxcommon)
endif()
diff --git a/src/plugins/simulator/fsxcommon/simconnectdatadefinition.cpp b/src/plugins/simulator/fsxcommon/simconnectdatadefinition.cpp
index 1ee57e99f..fd273e546 100644
--- a/src/plugins/simulator/fsxcommon/simconnectdatadefinition.cpp
+++ b/src/plugins/simulator/fsxcommon/simconnectdatadefinition.cpp
@@ -82,7 +82,7 @@ namespace swift::simplugin::fsxcommon
hr += initSimulatorEnvironment(hSimConnect);
hr += initSbDataArea(hSimConnect);
if (simInfo.isMSFS()) { hr += initMSFSTransponder(hSimConnect); }
- if (simInfo.isMSFS2024()) { hr += initMSFS2024Transponder(hSimConnect); }
+ // if (simInfo.isMSFS2024()) { hr += initMSFS2024Transponder(hSimConnect); }
return hr;
}
@@ -423,21 +423,21 @@ namespace swift::simplugin::fsxcommon
return hr;
}
- HRESULT CSimConnectDefinitions::initMSFS2024Transponder(const HANDLE hSimConnect)
- {
- HRESULT hr = s_ok();
- hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataTransponderModeMSFS,
- "TRANSPONDER STATE:1", "Enum");
- hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataTransponderModeMSFS,
- "TRANSPONDER IDENT:1", "Bool");
- if (isFailure(hr))
- {
- CLogMessage(static_cast(nullptr))
- .error(u"SimConnect error: MSFS2024 transponder data definitions %1")
- << hr;
- }
- return hr;
- }
+ // HRESULT CSimConnectDefinitions::initMSFS2024Transponder(const HANDLE hSimConnect)
+ //{
+ // HRESULT hr = s_ok();
+ // hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataTransponderModeMSFS,
+ // "TRANSPONDER STATE:1", "Enum");
+ // hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataTransponderModeMSFS,
+ // "TRANSPONDER IDENT:1", "Bool");
+ // if (isFailure(hr))
+ // {
+ // CLogMessage(static_cast(nullptr))
+ // .error(u"SimConnect error: MSFS2024 transponder data definitions %1")
+ // << hr;
+ // }
+ // return hr;
+ // }
DataDefinitionRemoteAircraftPartsWithoutLights::DataDefinitionRemoteAircraftPartsWithoutLights()
{
diff --git a/src/plugins/simulator/fsxcommon/simconnectdatadefinition.h b/src/plugins/simulator/fsxcommon/simconnectdatadefinition.h
index d23a3497c..9231e8f9c 100644
--- a/src/plugins/simulator/fsxcommon/simconnectdatadefinition.h
+++ b/src/plugins/simulator/fsxcommon/simconnectdatadefinition.h
@@ -262,7 +262,6 @@ namespace swift::simplugin::fsxcommon
ClientAreaSquawkBox
};
- // TODO TZ: are there any changes in MSFS2024
//! Handles SimConnect data definitions
class FSXCOMMON_EXPORT CSimConnectDefinitions
{
@@ -347,7 +346,7 @@ namespace swift::simplugin::fsxcommon
static HRESULT initMSFSTransponder(const HANDLE hSimConnect);
//! Initialize data definition for MSFS transponder
- static HRESULT initMSFS2024Transponder(const HANDLE hSimConnect);
+ // static HRESULT initMSFS2024Transponder(const HANDLE hSimConnect);
};
} // namespace swift::simplugin::fsxcommon
diff --git a/src/plugins/simulator/fsxcommon/simconnectsymbols.cpp b/src/plugins/simulator/fsxcommon/simconnectsymbols.cpp
index c2f12ef4b..3857f565d 100644
--- a/src/plugins/simulator/fsxcommon/simconnectsymbols.cpp
+++ b/src/plugins/simulator/fsxcommon/simconnectsymbols.cpp
@@ -351,37 +351,6 @@ bool loadAndResolveMSFSimConnect()
}
}
-bool loadAndResolveMSFS2024SimConnect()
-{
- // Check if already loaded
- if (gSymbols.SimConnect_Open) { return true; }
-
- QString simConnectFileName(QStringLiteral("SimConnect.MSFS2024"));
-
- QLibrary simConnectDll(simConnectFileName);
- simConnectDll.setLoadHints(QLibrary::PreventUnloadHint);
- if (simConnectDll.load())
- {
- const bool resolvedCommon = resolveCommonSimConnectSymbols(simConnectDll);
- if (!resolvedCommon)
- {
- CLogMessage(CLogCategories::driver()).error(u"Failed to resolve common symbols from SimConnect.dll: '%1'")
- << simConnectFileName;
- return false;
- }
-
- CLogMessage(CLogCategories::driver()).info(u"Loaded and resolved MSFS2024 symbols from SimConnect.dll: '%1'")
- << simConnectFileName;
- return resolvedCommon;
- }
- else
- {
- CLogMessage(CLogCategories::driver()).error(u"Failed to load SimConnect.dll: '%1' '%2'")
- << simConnectFileName << simConnectDll.errorString();
- return false;
- }
-}
-
#else
bool loadAndResolveFsxSimConnect(bool manifestProbing)
{
diff --git a/src/plugins/simulator/fsxcommon/simconnectsymbols.h b/src/plugins/simulator/fsxcommon/simconnectsymbols.h
index f8fe6c997..1dfeef909 100644
--- a/src/plugins/simulator/fsxcommon/simconnectsymbols.h
+++ b/src/plugins/simulator/fsxcommon/simconnectsymbols.h
@@ -39,7 +39,7 @@ inline bool loadAndResolveP3DSimConnectByString(const QString &version)
FSXCOMMON_EXPORT bool loadAndResolveMSFSimConnect();
-FSXCOMMON_EXPORT bool loadAndResolveMSFS2024SimConnect();
+// FSXCOMMON_EXPORT bool loadAndResolveMSFS2024SimConnect();
#else
diff --git a/src/plugins/simulator/fsxcommon/simconnectwindows.h b/src/plugins/simulator/fsxcommon/simconnectwindows.h
index 541c1cc0f..fd402e0cb 100644
--- a/src/plugins/simulator/fsxcommon/simconnectwindows.h
+++ b/src/plugins/simulator/fsxcommon/simconnectwindows.h
@@ -17,7 +17,9 @@
// clang-format off
#include
-#include
+//#include
+#include "../third_party/externals/common/include/simconnect/P3D-v4/SimConnect.h"
+
// clang-format on
#include
diff --git a/src/plugins/simulator/fsxcommon/simulatorfsxcommon.cpp b/src/plugins/simulator/fsxcommon/simulatorfsxcommon.cpp
index e130562a6..2e9966fdb 100644
--- a/src/plugins/simulator/fsxcommon/simulatorfsxcommon.cpp
+++ b/src/plugins/simulator/fsxcommon/simulatorfsxcommon.cpp
@@ -528,7 +528,6 @@ namespace swift::simplugin::fsxcommon
SIMCONNECT_PERIOD_SECOND, SIMCONNECT_DATA_REQUEST_FLAG_CHANGED),
"Cannot request title", Q_FUNC_INFO, "SimConnect_RequestDataOnSimObject");
- // TODO TZ use MSFS2024 FSUIPC?
if (!this->getSimulatorPluginInfo().getSimulatorInfo().isMSFS() &&
!this->getSimulatorPluginInfo().getSimulatorInfo().isMSFS2024())
{
diff --git a/src/plugins/simulator/msfs2024/CMakeLists.txt b/src/plugins/simulator/msfs2024/CMakeLists.txt
index d04f63955..6b7145be3 100644
--- a/src/plugins/simulator/msfs2024/CMakeLists.txt
+++ b/src/plugins/simulator/msfs2024/CMakeLists.txt
@@ -7,17 +7,27 @@ add_library(simulatormsfs2024 SHARED
simulatormsfs2024.json
simulatormsfs2024factory.cpp
simulatormsfs2024factory.h
-)
+ simconnectsymbolsmsfs2024.cpp
+ simconnectsymbolsmsfs2024.h
+ simulatormsfs2024common.cpp
+ simulatormsfs2024common.h
+ simconnectobjectmsfs2024.cpp
+ simconnectobjectmsfs2024.h
+ simconnectdatadefinitionmsfs2024.cpp
+ simconnectdatadefinitionmsfs2024.h
+ simulatormsfs2024simconnectproc.cpp
+ msfs2024export.h)
+
set_target_properties(simulatormsfs2024 PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/out/bin/plugins/simulator)
set_target_properties(simulatormsfs2024 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/out/bin/plugins/simulator)
target_include_directories(simulatormsfs2024 PUBLIC ${PROJECT_SOURCE_DIR}/src)
+target_compile_definitions(simulatormsfs2024 PRIVATE BUILD_MSFS2024_LIB)
target_link_libraries(simulatormsfs2024
PUBLIC
fscommon
- fsxcommon
core
misc
Qt::Core
diff --git a/src/plugins/simulator/msfs2024/msfs2024export.h b/src/plugins/simulator/msfs2024/msfs2024export.h
new file mode 100644
index 000000000..da7875508
--- /dev/null
+++ b/src/plugins/simulator/msfs2024/msfs2024export.h
@@ -0,0 +1,26 @@
+// SPDX-FileCopyrightText: Copyright (C) 2018 swift Project Community / Contributors
+// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
+
+//! \file
+
+#ifndef SWIFT_SIMPLUGIN_MSFS2024_SIMULATORMSFS2024_MACROS_H
+#define SWIFT_SIMPLUGIN_MSFS2024_SIMULATORMSFS2024_MACROS_H
+
+#include
+
+/*!
+ * \def MSFS2024_EXPORT
+ * MSFS2024 Export Macro
+ */
+
+#ifndef WITH_STATIC
+# if defined(BUILD_MSFS2024_LIB)
+# define MSFS2024_EXPORT Q_DECL_EXPORT
+# else
+# define MSFS2024_EXPORT Q_DECL_IMPORT
+# endif
+#else
+# define MSFS2024_EXPORT
+#endif
+
+#endif // SWIFT_SIMPLUGIN_MSFS2024_SIMULATORMSFS2024_MACROS_H
diff --git a/src/plugins/simulator/msfs2024/simconnectdatadefinitionmsfs2024.cpp b/src/plugins/simulator/msfs2024/simconnectdatadefinitionmsfs2024.cpp
new file mode 100644
index 000000000..7e1d45fe2
--- /dev/null
+++ b/src/plugins/simulator/msfs2024/simconnectdatadefinitionmsfs2024.cpp
@@ -0,0 +1,556 @@
+// SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors
+// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
+
+#include "simconnectdatadefinitionmsfs2024.h"
+
+#include
+
+#include
+
+#include "../fscommon/simulatorfscommonfunctions.h"
+#include "simconnectsymbolsmsfs2024.h"
+
+#include "misc/aviation/aircraftenginelist.h"
+#include "misc/aviation/aircraftparts.h"
+#include "misc/logmessage.h"
+
+using namespace swift::misc;
+using namespace swift::misc::aviation;
+using namespace swift::misc::simulation;
+using namespace swift::simplugin::fscommon;
+
+namespace swift::simplugin::msfs2024common
+{
+ const QString &CSimConnectDefinitions::requestToString(Request request)
+ {
+ static const QString ownAircraft("RequestOwnAircraft");
+ static const QString title("RequestOwnAircraftTitle");
+ static const QString livery("RequestOwnAircraftLivery");
+ static const QString sbData("RequestSbData");
+ static const QString facility("RequestFacility");
+ static const QString end("");
+ static const QString unknown("unknown");
+
+ switch (request)
+ {
+ case RequestOwnAircraft: return ownAircraft;
+ case RequestOwnAircraftTitle: return title;
+ case RequestSbData: return sbData;
+ case RequestFacility: return facility;
+ case RequestEndMarker: return end;
+ case RequestOwnAircraftLivery: return livery;
+ default: break;
+ }
+ return unknown;
+ }
+
+ const QString &CSimConnectDefinitions::simObjectRequestToString(SimObjectRequest simObjectRequest)
+ {
+ static const QString baseId("base id");
+ static const QString add("add");
+ static const QString remove("remove");
+ static const QString lights("lights");
+ static const QString pos("position");
+ static const QString model("model");
+ static const QString misc("misc");
+ static const QString end("");
+ static const QString unknown("unknown");
+
+ switch (simObjectRequest)
+ {
+ case SimObjectBaseId: return baseId;
+ case SimObjectAdd: return add;
+ case SimObjectRemove: return remove;
+ case SimObjectLights: return lights;
+ case SimObjectPositionData: return pos;
+ case SimObjectModel: return model;
+ case SimObjectMisc: return misc;
+ case SimObjectEndMarker: return end;
+ default: break;
+ }
+ return unknown;
+ }
+
+ CSimConnectDefinitions::CSimConnectDefinitions() {}
+
+ HRESULT CSimConnectDefinitions::initDataDefinitionsWhenConnected(const HANDLE hSimConnect,
+ const CSimulatorInfo &simInfo)
+ {
+ Q_UNUSED(simInfo);
+
+ HRESULT hr = s_ok();
+ hr += initOwnAircraft(hSimConnect);
+ hr += initRemoteAircraft(hSimConnect);
+ hr += initRemoteAircraftSimData(hSimConnect);
+ hr += initRemoteAircraftSimDataSet(hSimConnect);
+ hr += initSimulatorEnvironment(hSimConnect);
+ hr += initSbDataArea(hSimConnect);
+ hr += initMSFS2024Transponder(hSimConnect);
+ hr += initOwnAircraftList(hSimConnect);
+ return hr;
+ }
+
+ HRESULT CSimConnectDefinitions::initOwnAircraft(const HANDLE hSimConnect)
+ {
+ // MSFS2024 vars:
+ // https://docs.flightsimulator.com/msfs2024/html/6_Programming_APIs/SimVars/Simulation_Variables.htm
+ HRESULT hr = s_ok();
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "PLANE LATITUDE",
+ "Degrees");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "PLANE LONGITUDE",
+ "Degrees");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "PLANE ALTITUDE",
+ "Feet");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft,
+ "PLANE ALT ABOVE GROUND", "Feet");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "PRESSURE ALTITUDE",
+ "Meters");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft,
+ "STATIC CG TO GROUND", "Feet");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft,
+ "PLANE HEADING DEGREES TRUE", "Degrees");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft,
+ "PLANE PITCH DEGREES", "Degrees");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "PLANE BANK DEGREES",
+ "Degrees");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "GROUND VELOCITY",
+ "Knots");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "GROUND ALTITUDE",
+ "Feet");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "SIM ON GROUND",
+ "Bool");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "LIGHT STROBE",
+ "Bool");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "LIGHT LANDING",
+ "Bool");
+ hr +=
+ SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "LIGHT TAXI", "Bool");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "LIGHT BEACON",
+ "Bool");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "LIGHT NAV", "Bool");
+ hr +=
+ SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "LIGHT LOGO", "Bool");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "TRANSPONDER CODE:1",
+ nullptr);
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft,
+ "COM ACTIVE FREQUENCY:1", "MHz");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft,
+ "COM ACTIVE FREQUENCY:2", "MHz");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft,
+ "COM STANDBY FREQUENCY:1", "MHz");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft,
+ "COM STANDBY FREQUENCY:2", "MHz");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "COM TRANSMIT:1",
+ "Bool");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "COM TRANSMIT:2",
+ "Bool");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "COM RECEIVE ALL",
+ "Bool");
+ hr +=
+ SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "COM TEST:1", "Bool");
+ hr +=
+ SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "COM TEST:2", "Bool");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "COM STATUS:1",
+ "Enum");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "COM STATUS:2",
+ "Enum");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft,
+ "FLAPS HANDLE PERCENT", "Percent Over 100");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft,
+ "SPOILERS HANDLE POSITION", "Percent Over 100");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft,
+ "GEAR HANDLE POSITION", "Bool");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "NUMBER OF ENGINES",
+ "Number");
+ // Simconnect supports index 1 - 4
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft,
+ "GENERAL ENG COMBUSTION:1", "Bool");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft,
+ "GENERAL ENG COMBUSTION:2", "Bool");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft,
+ "GENERAL ENG COMBUSTION:3", "Bool");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft,
+ "GENERAL ENG COMBUSTION:4", "Bool");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "VELOCITY WORLD X",
+ "Feet per second");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "VELOCITY WORLD Y",
+ "Feet per second");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "VELOCITY WORLD Z",
+ "Feet per second");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft,
+ "ROTATION VELOCITY BODY X", "Radians per second");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft,
+ "ROTATION VELOCITY BODY Y", "Radians per second");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft,
+ "ROTATION VELOCITY BODY Z", "Radians per second");
+ // FS2020
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft,
+ "INDICATED ALTITUDE CALIBRATED", "Feet");
+
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraftTitle, "TITLE",
+ nullptr, SIMCONNECT_DATATYPE_STRING256);
+ // MSFS2024 specific from here
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraftTitle, "LIVERY NAME",
+ nullptr, SIMCONNECT_DATATYPE_STRING256);
+
+ if (isFailure(hr))
+ {
+ CLogMessage(static_cast(nullptr)).error(u"SimConnect error: initOwnAircraft %1")
+ << hr;
+ }
+ return hr;
+ }
+
+ HRESULT CSimConnectDefinitions::initRemoteAircraft(const HANDLE hSimConnect)
+ {
+ HRESULT hr = s_ok();
+ // Position
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftSetPosition,
+ "Initial Position", nullptr, SIMCONNECT_DATATYPE_INITPOSITION);
+
+ // Hint: "Bool" and "Percent .." are units name
+ // default data type is SIMCONNECT_DATATYPE_FLOAT64 -> double
+
+ // Flaps
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftPartsWithoutLights,
+ "LEADING EDGE FLAPS LEFT PERCENT", "Percent Over 100");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftPartsWithoutLights,
+ "LEADING EDGE FLAPS RIGHT PERCENT", "Percent Over 100");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftPartsWithoutLights,
+ "TRAILING EDGE FLAPS LEFT PERCENT", "Percent Over 100");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftPartsWithoutLights,
+ "TRAILING EDGE FLAPS RIGHT PERCENT", "Percent Over 100");
+
+ // Gear & Spoiler
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftPartsWithoutLights,
+ "GEAR HANDLE POSITION", "Bool");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftPartsWithoutLights,
+ "SPOILERS HANDLE POSITION", "Percent Over 100");
+
+ // Engines
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftPartsWithoutLights,
+ "GENERAL ENG COMBUSTION:1", "Bool");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftPartsWithoutLights,
+ "GENERAL ENG COMBUSTION:2", "Bool");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftPartsWithoutLights,
+ "GENERAL ENG COMBUSTION:3", "Bool");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftPartsWithoutLights,
+ "GENERAL ENG COMBUSTION:4", "Bool");
+
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraftTitle, "TITLE",
+ nullptr, SIMCONNECT_DATATYPE_STRING256);
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraftTitle, "LIVERY NAME",
+ nullptr, SIMCONNECT_DATATYPE_STRING256);
+
+ // Lights (other definition)
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftLights,
+ "LIGHT STROBE", "Bool");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftLights,
+ "LIGHT LANDING", "Bool");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftLights,
+ "LIGHT TAXI", "Bool");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftLights,
+ "LIGHT BEACON", "Bool");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftLights, "LIGHT NAV",
+ "Bool");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftLights,
+ "LIGHT LOGO", "Bool");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftLights, "LIGHT NAV",
+ "Bool");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftLights,
+ "LIGHT RECOGNITION", "Bool");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftLights,
+ "LIGHT CABIN", "Bool");
+
+ if (isFailure(hr))
+ {
+ CLogMessage(static_cast(nullptr))
+ .error(u"SimConnect error: initRemoteAircraftSituation %1")
+ << hr;
+ }
+ return hr;
+ }
+
+ HRESULT CSimConnectDefinitions::initRemoteAircraftSimData(const HANDLE hSimConnect)
+ {
+ HRESULT hr = s_ok();
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftGetPosition,
+ "PLANE LATITUDE", "degrees");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftGetPosition,
+ "PLANE LONGITUDE", "degrees");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftGetPosition,
+ "PLANE ALTITUDE", "Feet");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftGetPosition,
+ "GROUND ALTITUDE", "Feet");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftGetPosition,
+ "STATIC CG TO GROUND", "Feet");
+ if (isFailure(hr))
+ {
+ CLogMessage(static_cast(nullptr))
+ .error(u"SimConnect error: initRemoteAircraftSimData DataRemoteAircraftGetPosition %1")
+ << hr;
+ }
+
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftModelData,
+ "STATIC CG TO GROUND", "Feet");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftModelData,
+ "ATC TYPE", nullptr, SIMCONNECT_DATATYPE_STRING32);
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftModelData,
+ "ATC MODEL", nullptr, SIMCONNECT_DATATYPE_STRING32);
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftModelData, "ATC ID",
+ nullptr, SIMCONNECT_DATATYPE_STRING32);
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftModelData,
+ "ATC AIRLINE", nullptr, SIMCONNECT_DATATYPE_STRING64);
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftModelData,
+ "ATC FLIGHT NUMBER", nullptr, SIMCONNECT_DATATYPE_STRING8);
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftModelData, "TITLE",
+ nullptr, SIMCONNECT_DATATYPE_STRING256);
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftModelData,
+ "LIVERY NAME", nullptr, SIMCONNECT_DATATYPE_STRING256);
+
+ if (isFailure(hr))
+ {
+ CLogMessage(static_cast(nullptr))
+ .error(u"SimConnect error: initRemoteAircraftSimData DataRemoteAircraftModelData %1")
+ << hr;
+ }
+ return hr;
+ }
+
+ HRESULT CSimConnectDefinitions::initRemoteAircraftSimDataSet(const HANDLE hSimConnect)
+ {
+ HRESULT hr = s_ok();
+
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftSetData, "ATC ID",
+ nullptr, SIMCONNECT_DATATYPE_STRING32);
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftSetData,
+ "ATC AIRLINE", nullptr, SIMCONNECT_DATATYPE_STRING64);
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftSetData,
+ "ATC FLIGHT NUMBER", nullptr, SIMCONNECT_DATATYPE_STRING8);
+ if (isFailure(hr))
+ {
+ CLogMessage(static_cast(nullptr))
+ .error(u"SimConnect error: initRemoteAircraftSimDataSet DataRemoteAircraftModelData %1")
+ << hr;
+ }
+ return hr;
+ }
+
+ HRESULT CSimConnectDefinitions::initSimulatorEnvironment(const HANDLE hSimConnect)
+ {
+ HRESULT hr = s_ok();
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataSimEnvironment, "ZULU TIME", "",
+ SIMCONNECT_DATATYPE_INT32);
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataSimEnvironment, "ZULU YEAR", "",
+ SIMCONNECT_DATATYPE_INT32);
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataSimEnvironment,
+ "ZULU MONTH OF YEAR", "", SIMCONNECT_DATATYPE_INT32);
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataSimEnvironment,
+ "ZULU DAY OF MONTH", "", SIMCONNECT_DATATYPE_INT32);
+ if (isFailure(hr))
+ {
+ CLogMessage(static_cast(nullptr))
+ .error(u"SimConnect error: initSimulatorEnvironment %1")
+ << hr;
+ }
+ return hr;
+ }
+
+ HRESULT CSimConnectDefinitions::initSbDataArea(const HANDLE hSimConnect)
+ {
+ HRESULT hr = s_ok();
+ const DWORD sbSize = sizeof(DataDefinitionClientAreaSb);
+
+ // We need to know the client area 'name' and map it to a client ID
+ hr += SimConnect_MapClientDataNameToID(hSimConnect, "SquawkBox Data", ClientAreaSquawkBox);
+ if (isFailure(hr))
+ {
+ CLogMessage(static_cast(nullptr))
+ .error(u"SimConnect error: SimConnect_MapClientDataNameToID %1")
+ << hr;
+ return hr;
+ }
+
+ // Mapping needs to be first
+ hr += SimConnect_CreateClientData(hSimConnect, ClientAreaSquawkBox, sbSize,
+ SIMCONNECT_CREATE_CLIENT_DATA_FLAG_DEFAULT);
+ if (isFailure(hr))
+ {
+ CLogMessage(static_cast(nullptr))
+ .error(u"SimConnect error: SimConnect_CreateClientData %1")
+ << hr;
+ return hr;
+ }
+
+ // data definitions
+ hr += SimConnect_AddToClientDataDefinition(hSimConnect, CSimConnectDefinitions::DataClientAreaSb, 0,
+ sbSize); // whole area
+ hr += SimConnect_AddToClientDataDefinition(hSimConnect, CSimConnectDefinitions::DataClientAreaSbStandby, 17,
+ 1); // standby
+ hr += SimConnect_AddToClientDataDefinition(hSimConnect, CSimConnectDefinitions::DataClientAreaSbIdent, 19,
+ 1); // ident
+ hr += SimConnect_AddToClientDataDefinition(hSimConnect, CSimConnectDefinitions::DataClientAreaSbConnected, 1,
+ 1); // network connected
+ hr += SimConnect_AddToClientDataDefinition(hSimConnect, CSimConnectDefinitions::DataClientAreaSbRunning, 0,
+ 1); // SB running
+
+ if (isFailure(hr))
+ {
+ CLogMessage(static_cast(nullptr))
+ .error(u"SimConnect error: SB data area data definitions %1")
+ << hr;
+ return hr;
+ }
+
+ // write a default client area so we are not suddenly squawking ident or so
+ DataDefinitionClientAreaSb sbArea;
+ byte sbRunning = 1;
+ sbArea.setDefaultValues();
+ hr += SimConnect_SetClientData(hSimConnect, ClientAreaSquawkBox, CSimConnectDefinitions::DataClientAreaSb,
+ SIMCONNECT_CLIENT_DATA_SET_FLAG_DEFAULT, 0, sbSize, &sbArea);
+ hr +=
+ SimConnect_SetClientData(hSimConnect, ClientAreaSquawkBox, CSimConnectDefinitions::DataClientAreaSbRunning,
+ SIMCONNECT_CLIENT_DATA_SET_FLAG_DEFAULT, 0, 1, &sbRunning);
+ if (isFailure(hr))
+ {
+ CLogMessage(static_cast(nullptr))
+ .error(u"SimConnect error: SimConnect_SetClientData %1")
+ << hr;
+ }
+ return hr;
+ }
+
+ HRESULT CSimConnectDefinitions::initMSFS2024Transponder(const HANDLE hSimConnect)
+ {
+ HRESULT hr = s_ok();
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataTransponderModeMSFS,
+ "TRANSPONDER STATE:1", "Enum");
+ hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataTransponderModeMSFS,
+ "TRANSPONDER IDENT:1", "Bool");
+ if (isFailure(hr))
+ {
+ CLogMessage(static_cast(nullptr))
+ .error(u"SimConnect error: MSFS2024 transponder data definitions %1")
+ << hr;
+ }
+ return hr;
+ }
+
+ HRESULT CSimConnectDefinitions::initOwnAircraftList(const HANDLE hSimConnect)
+ {
+ // MSFS2024 vars:
+ // https://docs.flightsimulator.com/msfs2024/html/6_Programming_APIs/SimVars/Simulation_Variables.htm
+ HRESULT hr = s_ok();
+ hr += SimConnect_EnumerateSimObjectsAndLiveries(hSimConnect, CSimConnectDefinitions::REQUEST_AIRPLANE,
+ SIMCONNECT_SIMOBJECT_TYPE_AIRCRAFT);
+ hr += SimConnect_EnumerateSimObjectsAndLiveries(hSimConnect, CSimConnectDefinitions::REQUEST_HELICOPTER,
+ SIMCONNECT_SIMOBJECT_TYPE_AIRCRAFT);
+ // hr += SimConnect_EnumerateSimObjectsAndLiveries(hSimConnect, CSimConnectDefinitions::REQUEST_HOT_AIR,
+ // SIMCONNECT_SIMOBJECT_TYPE_AIRCRAFT);
+
+ if (isFailure(hr))
+ {
+ CLogMessage(static_cast(nullptr))
+ .error(u"SimConnect error: initOwnAircraftList %1")
+ << hr;
+ }
+ return hr;
+ }
+
+ DataDefinitionRemoteAircraftPartsWithoutLights::DataDefinitionRemoteAircraftPartsWithoutLights()
+ {
+ this->resetToInvalid();
+ }
+
+ DataDefinitionRemoteAircraftPartsWithoutLights::DataDefinitionRemoteAircraftPartsWithoutLights(
+ const CAircraftParts &parts)
+ {
+ this->initFromParts(parts);
+ }
+
+ bool DataDefinitionRemoteAircraftPartsWithoutLights::operator==(
+ const DataDefinitionRemoteAircraftPartsWithoutLights &rhs) const
+ {
+ return std::tie(flapsLeadingEdgeLeftPercent, flapsLeadingEdgeRightPercent, flapsTrailingEdgeLeftPercent,
+ flapsTrailingEdgeRightPercent, gearHandlePosition, spoilersHandlePosition, engine1Combustion,
+ engine2Combustion, engine3Combustion, engine4Combustion) ==
+ std::tie(rhs.flapsLeadingEdgeLeftPercent, rhs.flapsLeadingEdgeRightPercent,
+ rhs.flapsTrailingEdgeLeftPercent, rhs.flapsTrailingEdgeRightPercent, rhs.gearHandlePosition,
+ rhs.spoilersHandlePosition, rhs.engine1Combustion, rhs.engine2Combustion, rhs.engine3Combustion,
+ rhs.engine4Combustion);
+ }
+
+ void DataDefinitionRemoteAircraftPartsWithoutLights::setAllEngines(bool on)
+ {
+ engine1Combustion = on ? 1 : 0;
+ engine2Combustion = on ? 1 : 0;
+ engine3Combustion = on ? 1 : 0;
+ engine4Combustion = on ? 1 : 0;
+ }
+
+ void DataDefinitionRemoteAircraftPartsWithoutLights::setEngine(int number1based, bool on)
+ {
+ double v = on ? 1.0 : 0.0;
+ switch (number1based)
+ {
+ case 1: engine1Combustion = v; break;
+ case 2: engine2Combustion = v; break;
+ case 3: engine3Combustion = v; break;
+ case 4: engine4Combustion = v; break;
+ default: break;
+ }
+ }
+
+ void DataDefinitionRemoteAircraftPartsWithoutLights::resetAllFlaps()
+ {
+ flapsLeadingEdgeLeftPercent = 0.0;
+ flapsLeadingEdgeRightPercent = 0.0;
+ flapsTrailingEdgeLeftPercent = 0.0;
+ flapsTrailingEdgeRightPercent = 0.0;
+ }
+
+ void DataDefinitionRemoteAircraftPartsWithoutLights::resetSpoilers() { spoilersHandlePosition = 0.0; }
+
+ void DataDefinitionRemoteAircraftPartsWithoutLights::resetToInvalid()
+ {
+ flapsLeadingEdgeLeftPercent = -1;
+ flapsLeadingEdgeRightPercent = -1;
+ flapsTrailingEdgeLeftPercent = -1;
+ flapsTrailingEdgeRightPercent = -1;
+ gearHandlePosition = -1;
+ spoilersHandlePosition = -1;
+ engine1Combustion = -1;
+ engine2Combustion = -1;
+ engine3Combustion = -1;
+ engine4Combustion = -1;
+ }
+
+ void DataDefinitionRemoteAircraftPartsWithoutLights::initFromParts(const CAircraftParts &parts)
+ {
+ gearHandlePosition = parts.isFixedGearDown() ? 1.0 : 0.0;
+ const double trail = parts.getFlapsPercent() / 100.0;
+ const double lead = trail;
+ flapsTrailingEdgeLeftPercent = trail;
+ flapsTrailingEdgeRightPercent = trail;
+ flapsLeadingEdgeLeftPercent = lead;
+ flapsLeadingEdgeRightPercent = lead;
+ spoilersHandlePosition = parts.isSpoilersOut() ? 1.0 : 0.0;
+ this->setAllEngines(false); // init
+
+ int e = 1;
+ for (const CAircraftEngine &engine : parts.getEngines()) { this->setEngine(e++, engine.isOn()); }
+ }
+
+ CAircraftLights DataDefinitionRemoteAircraftLights::toLights() const
+ {
+ return CAircraftLights(dtb(lightStrobe), dtb(lightLanding), dtb(lightTaxi), dtb(lightBeacon), dtb(lightNav),
+ dtb(lightLogo), dtb(lightRecognition), dtb(lightCabin));
+ }
+
+ QString DataDefinitionClientAreaSb::toQString() const
+ {
+ return u"0 (running): " % QString::number(data[0]) % u" 1 (connected): " % QString::number(data[1]) %
+ u" 17 (standby): " % QString::number(data[17]) % u" 19 (ident): " % QString::number(data[19]);
+ }
+
+} // namespace swift::simplugin::msfs2024common
diff --git a/src/plugins/simulator/msfs2024/simconnectdatadefinitionmsfs2024.h b/src/plugins/simulator/msfs2024/simconnectdatadefinitionmsfs2024.h
new file mode 100644
index 000000000..1c2bd4571
--- /dev/null
+++ b/src/plugins/simulator/msfs2024/simconnectdatadefinitionmsfs2024.h
@@ -0,0 +1,383 @@
+// SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors
+// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
+
+//! \file
+
+#ifndef SWIFT_SIMPLUGIN_MSFS2024COMMON_SIMCONNECT_DATADEFINITION_H
+#define SWIFT_SIMPLUGIN_MSFS2024COMMON_SIMCONNECT_DATADEFINITION_H
+
+#include
+
+#include
+#include
+
+#include "misc/aviation/aircraftlights.h"
+#include "misc/simulation/simulatorinfo.h"
+#include "plugins/simulator/msfs2024/msfs2024export.h"
+#include "plugins/simulator/msfs2024/simconnectsymbolsmsfs2024.h"
+#include "plugins/simulator/msfs2024/simconnectwindowsmsfs2024.h"
+
+namespace swift::misc::aviation
+{
+ class CAircraftParts;
+}
+namespace swift::simplugin::msfs2024common
+{
+ //! Data struct of our own aircraft
+ //! \sa SimConnect variables http://msdn.microsoft.com/en-us/library/cc526981.aspx
+ //! \sa SimConnect events http://msdn.microsoft.com/en-us/library/cc526980.aspx
+ struct DataDefinitionOwnAircraft
+ {
+ double latitudeDeg; //!< Latitude (deg)
+ double longitudeDeg; //!< Longitude (deg)
+ double altitudeFt; //!< Altitude (ft)
+ double altitudeAGLFt; //!< Altitude above ground (ft)
+ double pressureAltitudeM; //!< Pressure altitude (m)
+ double cgToGroundFt; //!< Static CG to ground (ft)
+ double trueHeadingDeg; //!< True heading (deg)
+ double pitchDeg; //!< Pitch (deg)
+ double bankDeg; //!< Bank (deg)
+ double velocity; //!< Ground velocity
+ double elevationFt; //!< Elevation (ft)
+ double simOnGround; //!< Is aircraft on ground?
+ // 12
+ double lightStrobe; //!< Is strobe light on?
+ double lightLanding; //!< Is landing light on?
+ double lightTaxi; //!< Is taxi light on?
+ double lightBeacon; //!< Is beacon light on?
+ double lightNav; //!< Is nav light on?
+ double lightLogo; //!< Is logo light on?
+ // 18
+ double transponderCode; //!< Transponder Code
+ double com1ActiveMHz; //!< COM1 active frequency
+ double com2ActiveMHz; //!< COM2 active frequency
+ double com1StandbyMHz; //!< COM1 standby frequency
+ double com2StandbyMHz; //!< COM2 standby frequency
+ double comTransmit1; //!< COM1 transmit, means also receiving
+ double comTransmit2; //!< COM2 transmit, means also receiving
+ double comReceiveAll; //!< all COMs receiving, or COM:x transmitting or receiving
+ double comTest1; //!< COM1 test
+ double comTest2; //!< COM2 test
+ double comStatus1; //!< COM1 status
+ double comStatus2; //!< COM2 status
+ // 30
+ double flapsHandlePosition; //!< Flaps handle position in percent
+ double spoilersHandlePosition; //!< Spoilers out? (flag)
+ double gearHandlePosition; //!< Gear handle position (flag)
+ // 33
+ double numberOfEngines; //!< Number of engines
+ double engine1Combustion; //!< Engine 1 combustion flag
+ double engine2Combustion; //!< Engine 2 combustion flag
+ double engine3Combustion; //!< Engine 3 combustion flag
+ double engine4Combustion; //!< Engine 4 combustion flag
+ // 38
+ double velocityWorldX; //!< Velocity World X
+ double velocityWorldY; //!< Velocity World Y
+ double velocityWorldZ; //!< Velocity World Z
+ double rotationVelocityBodyX; //!< Rotation Velocity Body X
+ double rotationVelocityBodyY; //!< Rotation Velocity Body Y
+ double rotationVelocityBodyZ; //!< Rotation Velocity Body Z
+ // 44
+ double altitudeCalibratedFt; //!< Altitude without temperature effect (ft, FS2020)
+ // 45
+ };
+
+ //! Data struct of aircraft position
+ struct DataDefinitionOwnAircraftModel
+ {
+ char title[256]; //!< Aircraft model string
+ char livery[256]; //!< Aircraft model string
+ };
+
+ //! Data struct of aircraft model data
+
+ // struct DataDefinitionOwnAircraftLivery
+ //{
+ // char livery[256]; //!< Aircraft model string
+ // };
+
+ ////! Data struct of aircraft model livery
+
+ struct DataDefinitionRemoteAircraftModel
+ {
+ double cgToGroundFt; //!< Static CG to ground (ft)
+ char atcType[32]; //!< type
+ char atcModel[32]; //!< model
+ char atcId[32]; //!< id
+ char atcAirlineNumber[64]; //!< airline number
+ char atcFlightNumber[8]; //!< flight number (168)
+ char title[256]; //!< Aircraft model string
+ };
+
+ //! Data struct of aircraft data (setable)
+ struct DataDefinitionRemoteAtc
+ {
+ // length here is from SimConnect_AddToDataDefinition
+ char atcId[32]; //!< ID used by ATC
+ char atcAirline[64]; //!< Airline used by ATC
+ char atcFlightNumber[8]; //!< Flight Number used by ATC
+
+ //! @{
+ //! Copy the strings, length from docu
+ void copyAtcId(const char *c)
+ {
+ strncpy_s(atcId, c, 10);
+ atcId[9] = 0;
+ }
+ void copyAtcAirline(const char *c)
+ {
+ strncpy_s(atcAirline, c, 50);
+ atcAirline[49] = 0;
+ }
+ void copyFlightNumber(const char *c)
+ {
+ strncpy_s(atcFlightNumber, c, 6);
+ atcFlightNumber[5] = 0;
+ }
+ //! @}
+
+ //! Set default values
+ void setDefaultValues()
+ {
+ std::fill(atcId, atcId + 10, static_cast(0));
+ std::fill(atcAirline, atcAirline + 50, static_cast(0));
+ std::fill(atcFlightNumber, atcFlightNumber + 6, static_cast(0));
+ }
+ };
+
+ //! Data struct of remote aircraft parts
+ struct MSFS2024_EXPORT DataDefinitionRemoteAircraftPartsWithoutLights
+ {
+ double flapsLeadingEdgeLeftPercent; //!< Leading edge left in percent 0..1
+ double flapsLeadingEdgeRightPercent; //!< Leading edge right in percent 0..1
+ double flapsTrailingEdgeLeftPercent; //!< Trailing edge left in percent 0..1
+ double flapsTrailingEdgeRightPercent; //!< Trailing edge right in percent 0..1
+ double gearHandlePosition; //!< Gear handle position
+ double spoilersHandlePosition; //!< Spoilers out?
+ double engine1Combustion; //!< Engine 1 combustion flag
+ double engine2Combustion; //!< Engine 2 combustion flag
+ double engine3Combustion; //!< Engine 3 combustion flag
+ double engine4Combustion; //!< Engine 4 combustion flag
+
+ //! Ctor
+ DataDefinitionRemoteAircraftPartsWithoutLights();
+
+ //! Ctor
+ DataDefinitionRemoteAircraftPartsWithoutLights(const swift::misc::aviation::CAircraftParts &parts);
+
+ //! Equal to other parts
+ bool operator==(const DataDefinitionRemoteAircraftPartsWithoutLights &rhs) const;
+
+ //! All engines on/off
+ void setAllEngines(bool on);
+
+ //! Set given engine
+ void setEngine(int number1based, bool on);
+
+ //! Reset all flaps
+ void resetAllFlaps();
+
+ //! Reset spoilers
+ void resetSpoilers();
+
+ //! Reset to invalid values
+ void resetToInvalid();
+
+ //! Init from parts
+ void initFromParts(const swift::misc::aviation::CAircraftParts &parts);
+ };
+
+ //! Data for aircraft lighs
+ struct MSFS2024_EXPORT DataDefinitionRemoteAircraftLights
+ {
+ double lightStrobe; //!< Is strobe light on?
+ double lightLanding; //!< Is landing light on?
+ double lightTaxi; //!< Is taxi light on?
+ double lightBeacon; //!< Is beacon light on?
+ double lightNav; //!< Is nav light on?
+ double lightLogo; //!< Is logo light on?
+ double lightRecognition; //!< Is recognition light on
+ double lightCabin; //!< Is cabin light on
+
+ //! Convert to lights
+ swift::misc::aviation::CAircraftLights toLights() const;
+ };
+
+ //! Data for AI object and probe sent back from simulator
+ struct DataDefinitionPosData
+ {
+ double latitudeDeg; //!< Latitude (deg)
+ double longitudeDeg; //!< Longitude (deg)
+ double altitudeFt; //!< Altitude (ft)
+ double elevationFt; //!< Elevation (ft)
+ double cgToGroundFt; //!< Static CG to ground (ft)
+
+ //! Above ground ft
+ double aboveGroundFt() const { return altitudeFt - elevationFt; }
+
+ //! Above ground ft
+ bool isOnGround() const { return this->aboveGroundFt() < 1.0; }
+ };
+
+ //! The whole SB data area
+ //! \remark vPilot SB area https://forums.vatsim.net/viewtopic.php?p=519580
+ //! \remark SB offsets http://www.squawkbox.ca/doc/sdk/fsuipc.php
+ struct DataDefinitionClientAreaSb
+ {
+ byte data[128] {}; //!< 128 bytes of data, offsets
+
+ //! Standby = 1, else 0
+ byte getTransponderMode() const { return data[17]; }
+
+ //! Ident = 1, else 0
+ byte getIdent() const { return data[19]; }
+
+ //! Ident?
+ bool isIdent() const { return getIdent() != 0; }
+
+ //! Standby
+ bool isStandby() const { return getTransponderMode() != 0; }
+
+ //! SB is running
+ void setRunning(bool running) { data[0] = running ? 1 : 0; }
+
+ //! Mark as connected with network
+ void setConnected(bool connected) { data[1] = connected ? 1 : 0; }
+
+ //! Set default values
+ void setDefaultValues()
+ {
+ std::fill(data, data + 128, static_cast(0));
+ data[0] = 1; // SB running, indicates the client is running as external app, 0..not running, 1..external
+ // app, 2..FS module
+ data[1] = 0; // SB connected to FSD, 0..not connected, 1..connected
+ data[17] = 1; // 1..standby, 0..mode C
+ data[19] = 0; // no ident
+ }
+
+ //! Values
+ QString toQString() const;
+ };
+
+ //! Data structure for MSFS transponder mode information
+ struct DataDefinitionMSFSTransponderMode
+ {
+ double transponderMode = 1; //!< transponder state simvar
+ double ident = 0; //!< ident
+ };
+
+ //! Client areas
+ enum ClientAreaId
+ {
+ ClientAreaSquawkBox
+ };
+
+ //! Handles SimConnect data definitions for MSFS2024
+ class MSFS2024_EXPORT CSimConnectDefinitions
+ {
+ public:
+ //! SimConnect definiton IDs
+ enum DataDefiniton
+ {
+ DataOwnAircraft,
+ DataOwnAircraftTitle,
+ DataRemoteAircraftLights,
+ DataRemoteAircraftPartsWithoutLights,
+ DataRemoteAircraftSetPosition, //!< the position which will be set
+ DataRemoteAircraftGetPosition, //!< get position to evaluate altitude / AGL
+ DataRemoteAircraftModelData, //!< model data eventually used and reported back from simulator
+ DataRemoteAircraftSetData, //!< set model data such as airline
+ DataSimEnvironment,
+ DataTransponderModeMSFS,
+ DataClientAreaSb, //!< whole SB area, see http://squawkbox.ca/doc/sdk/fsuipc.php
+ DataClientAreaSbIdent, //!< SB ident single value 0x7b93/19
+ DataClientAreaSbStandby, //!< SB standby 0x7b91/17
+ DataClientAreaSbConnected, //!< SB connected with network 0x7b81/1
+ DataClientAreaSbRunning //!< SB running 0x7b80/0
+ };
+
+ //! SimConnect request IDs
+ enum Request
+ {
+ RequestOwnAircraft,
+ RequestOwnAircraftTitle,
+ RequestOwnAircraftLivery,
+ RequestSbData, //!< SB client area / XPDR mode
+ RequestMSFSTransponder, //!< MSFS XPDR mode/ident
+ RequestFacility,
+ RequestEndMarker, //!< free request ids can start here
+
+ };
+
+ //! SimObject requests used for AI aircraft and probes
+ enum SimObjectRequest
+ {
+ SimObjectBaseId, //!< base id without specific request
+ SimObjectAdd,
+ SimObjectRemove,
+ SimObjectPositionData,
+ SimObjectLights,
+ SimObjectModel,
+ SimObjectMisc,
+ SimObjectEndMarker //!< end marker, do NOT remove, also means invalid
+ };
+
+ enum REQUEST_ID
+ {
+ REQUEST_NONE = 0,
+ REQUEST_POSITION_USER = 50,
+ REQUEST_CREATE = 100,
+ REQUEST_ALL = 1000,
+ REQUEST_USER,
+ REQUEST_AIRPLANE,
+ REQUEST_HELICOPTER,
+ REQUEST_GROUND,
+ REQUEST_ANIMAL,
+ REQUEST_HOT_AIR,
+ REQUEST_BOAT,
+ };
+
+ //! Request to string
+ static const QString &requestToString(Request request);
+
+ //! Request to string
+ static const QString &simObjectRequestToString(SimObjectRequest simObjectRequest);
+
+ //! Constructor
+ CSimConnectDefinitions();
+
+ //! Initialize all data definitions
+ static HRESULT initDataDefinitionsWhenConnected(const HANDLE hSimConnect,
+ const swift::misc::simulation::CSimulatorInfo &simInfo);
+
+ //! Initialize data retrieval for model list
+ static HRESULT initOwnAircraftList(const HANDLE hSimConnect);
+
+ private:
+ //! Initialize data definition for our own aircraft
+ static HRESULT initOwnAircraft(const HANDLE hSimConnect);
+
+ //! Initialize data definition for remote aircraft
+ static HRESULT initRemoteAircraft(const HANDLE hSimConnect);
+
+ //! Initialize data for setting remote aircraft airline etc.
+ static HRESULT initRemoteAircraftSimData(const HANDLE hSimConnect);
+
+ //! Initialize data for remote aircraft queried from simulator
+ static HRESULT initRemoteAircraftSimDataSet(const HANDLE hSimConnect);
+
+ //! Initialize data definition for Simulator environment
+ static HRESULT initSimulatorEnvironment(const HANDLE hSimConnect);
+
+ //! Initialize the SB data are
+ static HRESULT initSbDataArea(const HANDLE hSimConnect);
+
+ //! Initialize data definition for MSFS transponder
+ // static HRESULT initMSFSTransponder(const HANDLE hSimConnect);
+
+ //! Initialize data definition for MSFS transponder
+ static HRESULT initMSFS2024Transponder(const HANDLE hSimConnect);
+ };
+} // namespace swift::simplugin::msfs2024common
+
+#endif // SWIFT_SIMPLUGIN_MSFS2024COMMON_SIMCONNECT_DATADEFINITION_H
diff --git a/src/plugins/simulator/msfs2024/simconnectobjectmsfs2024.cpp b/src/plugins/simulator/msfs2024/simconnectobjectmsfs2024.cpp
new file mode 100644
index 000000000..1d2251fe5
--- /dev/null
+++ b/src/plugins/simulator/msfs2024/simconnectobjectmsfs2024.cpp
@@ -0,0 +1,547 @@
+// SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors
+// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
+
+#include "simconnectobjectmsfs2024.h"
+
+#include "simulatormsfs2024common.h"
+
+#include "config/buildconfig.h"
+#include "core/simulator.h"
+#include "misc/simulation/interpolation/interpolatormulti.h"
+#include "misc/stringutils.h"
+
+using namespace swift::config;
+using namespace swift::misc;
+using namespace swift::misc::aviation;
+using namespace swift::misc::simulation;
+using namespace swift::core;
+
+namespace swift::simplugin::msfs2024common
+{
+ CSimConnectObject::CSimConnectObject() { this->resetCameraPositions(); }
+
+ CSimConnectObject::CSimConnectObject(CSimConnectObject::SimObjectType type) : m_type(type)
+ {
+ this->resetCameraPositions();
+ }
+
+ CSimConnectObject::CSimConnectObject(const CSimulatedAircraft &aircraft, DWORD requestId,
+ ISimulationEnvironmentProvider *simEnvProvider,
+ IInterpolationSetupProvider *setupProvider,
+ IRemoteAircraftProvider *remoteAircraftProvider, CInterpolationLogger *logger)
+ : m_aircraft(aircraft), m_requestId(requestId), m_validRequestId(true),
+ m_interpolator(QSharedPointer::create(aircraft.getCallsign(), simEnvProvider,
+ setupProvider, remoteAircraftProvider, logger))
+ {
+ this->resetCameraPositions();
+ m_type = aircraft.isTerrainProbe() ? TerrainProbe : AircraftNonAtc;
+ m_interpolator->initCorrespondingModel(aircraft.getModel());
+ m_callsignByteArray = aircraft.getCallsignAsString().toLatin1();
+ }
+
+ void CSimConnectObject::setAircraft(const CSimulatedAircraft &aircraft)
+ {
+ m_aircraft = aircraft;
+ m_callsignByteArray = aircraft.getCallsignAsString().toLatin1();
+ m_type = aircraft.isTerrainProbe() ? TerrainProbe : AircraftNonAtc;
+ }
+
+ void CSimConnectObject::setAircraftModelString(const QString &modelString)
+ {
+ if (modelString.isEmpty()) { return; }
+ m_aircraft.setModelString(modelString);
+ }
+
+ void CSimConnectObject::setAircraftCG(const physical_quantities::CLength &cg)
+ {
+ if (cg.isNull()) { return; }
+ m_aircraft.setCG(cg);
+ }
+
+ void CSimConnectObject::setRequestId(DWORD id)
+ {
+ m_requestId = id;
+ m_validRequestId = true;
+ const SimObjectType type = requestIdToType(id);
+ this->setType(type);
+ }
+
+ DWORD CSimConnectObject::getRequestId(CSimConnectDefinitions::SimObjectRequest offset) const
+ {
+ if (CBuildConfig::isLocalDeveloperDebugBuild())
+ {
+ const SimObjectType type = requestIdToType(m_requestId);
+ const bool same = CSimConnectObject::isSameTypeGroup(type, this->getType());
+ Q_ASSERT_X(same, Q_FUNC_INFO, "Type mismatch");
+ }
+
+ DWORD os = 0;
+ switch (this->getType())
+ {
+ case TerrainProbe: os = static_cast(CSimulatorMsfs2024::offsetSimObjTerrainProbe(offset)); break;
+ case AircraftNonAtc:
+ case AircraftSimulatedObject:
+ default: os = static_cast(CSimulatorMsfs2024::offsetSimObjAircraft(offset)); break;
+ }
+ return os + m_requestId;
+ }
+
+ void CSimConnectObject::setObjectId(DWORD id)
+ {
+ m_objectId = id;
+ m_validObjectId = true;
+ }
+
+ bool CSimConnectObject::isPendingAdded() const { return !this->hasValidRequestAndObjectId() || !m_confirmedAdded; }
+
+ bool CSimConnectObject::isOutdatedPendingAdded(qint64 thresholdMs, qint64 currentMsSinceEpoch) const
+ {
+ if (!this->isPendingAdded()) { return false; }
+ if (currentMsSinceEpoch < 0) { currentMsSinceEpoch = QDateTime::currentMSecsSinceEpoch(); }
+ if (m_tsCreated < 0) { return true; } // no valid timestamp
+ const qint64 delta = currentMsSinceEpoch - m_tsCreated;
+ return delta > thresholdMs;
+ }
+
+ bool CSimConnectObject::isConfirmedAdded() const
+ {
+ Q_ASSERT_X(!m_confirmedAdded || this->hasValidRequestAndObjectId(), Q_FUNC_INFO, "confirmed but invalid ids");
+ return m_confirmedAdded;
+ }
+
+ void CSimConnectObject::setConfirmedAdded(bool confirm)
+ {
+ m_confirmedAdded = confirm;
+ m_removedWhileAdding = false;
+ m_addedWhileRemoving = false;
+ m_aircraft.setRendered(true);
+ }
+
+ void CSimConnectObject::setAddedWhileRemoving(bool addedWileRemoved) { m_addedWhileRemoving = addedWileRemoved; }
+
+ void CSimConnectObject::setRemovedWhileAdding(bool removedWhileAdding)
+ {
+ m_removedWhileAdding = removedWhileAdding;
+ }
+
+ bool CSimConnectObject::isReadyToSend() const
+ {
+ return !this->isPending() && !m_addedWhileRemoving && !m_removedWhileAdding;
+ }
+
+ void CSimConnectObject::setPendingRemoved(bool pending)
+ {
+ m_pendingRemoved = pending;
+ m_removedWhileAdding = false;
+ m_addedWhileRemoving = false;
+ m_aircraft.setRendered(false);
+ }
+
+ void CSimConnectObject::resetCameraPositions()
+ {
+ m_cameraPosition.x = 0;
+ m_cameraPosition.y = 0;
+ m_cameraPosition.z = 0;
+ m_cameraRotation.Pitch = 0;
+ m_cameraRotation.Bank = 0;
+ m_cameraRotation.Heading = 0;
+ }
+
+ void CSimConnectObject::resetState()
+ {
+ m_pendingRemoved = false;
+ m_confirmedAdded = false;
+ m_removedWhileAdding = false;
+ m_addedWhileRemoving = false;
+ m_camera = false;
+ m_currentLightsInSim = CAircraftLights();
+ m_lightsAsSent = CAircraftLights();
+ m_requestId = 0;
+ m_objectId = 0;
+ m_addingExceptions = 0;
+ m_validRequestId = false;
+ m_validObjectId = false;
+ m_tsCreated = -1;
+ this->resetCameraPositions();
+ }
+
+ void CSimConnectObject::resetToAddAgain()
+ {
+ const CSimConnectObject old(*this);
+ this->resetState();
+ this->copyAddingFailureCounters(old);
+ }
+
+ bool CSimConnectObject::hasValidRequestAndObjectId() const
+ {
+ return this->hasValidRequestId() && this->hasValidObjectId();
+ }
+
+ void CSimConnectObject::copyAddingFailureCounters(const CSimConnectObject &otherObject)
+ {
+ m_addingExceptions = otherObject.m_addingExceptions;
+ m_addingDirectlyRemoved = otherObject.m_addingDirectlyRemoved;
+ }
+
+ QString CSimConnectObject::getInterpolatorInfo(CInterpolationAndRenderingSetupBase::InterpolatorMode mode) const
+ {
+ Q_ASSERT(m_interpolator);
+ return m_interpolator->getInterpolatorInfo(mode);
+ }
+
+ void CSimConnectObject::attachInterpolatorLogger(CInterpolationLogger *logger) const
+ {
+ Q_ASSERT(m_interpolator);
+ m_interpolator->attachLogger(logger);
+ }
+
+ CInterpolationResult CSimConnectObject::getInterpolation(qint64 currentTimeSinceEpoch,
+ const CInterpolationAndRenderingSetupPerCallsign &setup,
+ uint32_t aircraftNumber) const
+ {
+ if (!m_interpolator) { return {}; }
+ return m_interpolator->getInterpolation(currentTimeSinceEpoch, setup, aircraftNumber);
+ }
+
+ const CAircraftSituation &
+ CSimConnectObject::getLastInterpolatedSituation(CInterpolationAndRenderingSetupBase::InterpolatorMode mode) const
+ {
+ if (!m_interpolator) { return CAircraftSituation::null(); }
+ return m_interpolator->getLastInterpolatedSituation(mode);
+ }
+
+ const CStatusMessageList &
+ CSimConnectObject::getInterpolationMessages(CInterpolationAndRenderingSetupBase::InterpolatorMode mode) const
+ {
+ static const CStatusMessageList empty;
+ if (!m_interpolator) { return empty; }
+ return m_interpolator->getInterpolationMessages(mode);
+ }
+
+ QString CSimConnectObject::toQString() const
+ {
+ static const QString s(
+ "CS: '%1' obj: %2 req: %3 conf.added: %4 pend.rem.: %5 rwa: %6 awr: %7 aEx: %8 aRem: %9");
+ return s.arg(this->getCallsign().asString())
+ .arg(m_objectId)
+ .arg(m_requestId)
+ .arg(boolToYesNo(m_confirmedAdded), boolToYesNo(m_pendingRemoved), boolToYesNo(m_removedWhileAdding),
+ boolToYesNo(m_addedWhileRemoving))
+ .arg(m_addingExceptions)
+ .arg(m_addingDirectlyRemoved);
+ }
+
+ CSimConnectObject::SimObjectType CSimConnectObject::requestIdToType(DWORD requestId)
+ {
+ if (CSimulatorMsfs2024::isRequestForSimObjTerrainProbe(requestId)) { return TerrainProbe; }
+ if (CSimulatorMsfs2024::isRequestForSimObjAircraft(requestId)) { return AircraftNonAtc; }
+ Q_ASSERT_X(false, Q_FUNC_INFO, "Wrong range");
+ return AircraftNonAtc;
+ }
+
+ const QString &CSimConnectObject::typeToString(CSimConnectObject::SimObjectType type)
+ {
+ static const QString a1("aircraft (non ATC)");
+ static const QString a2("aircraft (sim.object)");
+ static const QString p("probe");
+ static const QString u("unknown");
+ switch (type)
+ {
+ case AircraftNonAtc: return a1;
+ case AircraftSimulatedObject: return a2;
+ case TerrainProbe: return p;
+ default: break;
+ }
+ return u;
+ }
+
+ bool CSimConnectObject::isSameTypeGroup(CSimConnectObject::SimObjectType t1, CSimConnectObject::SimObjectType t2)
+ {
+ if (t1 == t2) { return true; }
+ return isAircraft(t1) && isAircraft(t2);
+ }
+
+ bool CSimConnectObject::isAircraft(CSimConnectObject::SimObjectType type)
+ {
+ return CSimConnectObject::AircraftNonAtc == type || CSimConnectObject::AircraftSimulatedObject;
+ }
+
+ bool CSimConnectObjects::insert(const CSimConnectObject &simObject, bool updateTimestamp)
+ {
+ if (!simObject.hasCallsign()) { return false; }
+ if (updateTimestamp)
+ {
+ CSimConnectObject simObj(simObject);
+ simObj.resetTimestampToNow();
+ (*this)[simObj.getCallsign()] = simObj;
+ }
+ else { (*this)[simObject.getCallsign()] = simObject; }
+ return true;
+ }
+
+ bool CSimConnectObjects::setSimConnectObjectIdForRequestId(DWORD requestId, DWORD objectId)
+ {
+ // First check, if this request id belongs to us
+ auto it = std::find_if(this->begin(), this->end(),
+ [requestId](const CSimConnectObject &obj) { return obj.getRequestId() == requestId; });
+ if (it == this->end()) { return false; }
+
+ // belongs to us
+ it->setObjectId(objectId);
+ return true;
+ }
+
+ CCallsign CSimConnectObjects::getCallsignForObjectId(DWORD objectId) const
+ {
+ return this->getSimObjectForObjectId(objectId).getCallsign();
+ }
+
+ CCallsignSet CSimConnectObjects::getAllCallsigns(bool withoutProbes) const
+ {
+ if (this->isEmpty()) { return CCallsignSet(); }
+ if (!withoutProbes) { return CCallsignSet(this->keys()); }
+ CCallsignSet callsigns;
+ for (const CSimConnectObject &simObject : *this)
+ {
+ if (simObject.isAircraft()) { callsigns.insert(simObject.getCallsign().asString()); }
+ }
+ return callsigns;
+ }
+
+ QStringList CSimConnectObjects::getAllCallsignStrings(bool sorted, bool withoutProbes) const
+ {
+ return this->getAllCallsigns(withoutProbes).getCallsignStrings(sorted);
+ }
+
+ QString CSimConnectObjects::getAllCallsignStringsAsString(bool sorted, const QString &separator) const
+ {
+ return this->getAllCallsignStrings(sorted).join(separator);
+ }
+
+ CSimConnectObject CSimConnectObjects::getSimObjectForObjectId(DWORD objectId) const
+ {
+ for (const CSimConnectObject &simObject : *this)
+ {
+ if (simObject.getObjectId() == objectId) { return simObject; }
+ }
+ return CSimConnectObject();
+ }
+
+ CSimConnectObject CSimConnectObjects::getOldestObject() const
+ {
+ if (this->isEmpty()) { return CSimConnectObject(); }
+ CSimConnectObject oldestSimObj = *this->begin();
+ for (const CSimConnectObject &simObj : *this)
+ {
+ if (!simObj.hasCreatedTimestamp()) { continue; }
+ if (!oldestSimObj.hasCreatedTimestamp() ||
+ oldestSimObj.getCreatedTimestamp() > simObj.getCreatedTimestamp())
+ {
+ oldestSimObj = simObj;
+ }
+ }
+ return oldestSimObj;
+ }
+
+ CSimConnectObject CSimConnectObjects::getSimObjectForRequestId(DWORD requestId) const
+ {
+ for (const CSimConnectObject &simObject : *this)
+ {
+ if (simObject.getRequestId() == requestId) { return simObject; }
+ }
+ return CSimConnectObject();
+ }
+
+ CSimConnectObject CSimConnectObjects::getSimObjectForOtherSimObject(const CSimConnectObject &otherSimObj) const
+ {
+ if (otherSimObj.hasValidObjectId())
+ {
+ CSimConnectObject obj = this->getSimObjectForObjectId(otherSimObj.getObjectId());
+ if (!obj.isInvalid()) { return obj; }
+ }
+ if (!otherSimObj.hasValidRequestId()) { return CSimConnectObject(); }
+ return this->getSimObjectForRequestId(otherSimObj.getRequestId());
+ }
+
+ bool CSimConnectObjects::isKnownSimObjectId(DWORD objectId) const
+ {
+ const CSimConnectObject simObject(this->getSimObjectForObjectId(objectId));
+ return simObject.hasValidRequestAndObjectId() && objectId == simObject.getObjectId();
+ }
+
+ bool CSimConnectObjects::removeByObjectId(DWORD objectId)
+ {
+ const CSimConnectObject simObject(this->getSimObjectForObjectId(objectId));
+ const int c = this->remove(simObject.getCallsign());
+ return c > 0;
+ }
+
+ bool CSimConnectObjects::removeByOtherSimObject(const CSimConnectObject &otherSimObj)
+ {
+ const int c = this->remove(otherSimObj.getCallsign());
+ return c > 0;
+ }
+
+ int CSimConnectObjects::removeAllProbes()
+ {
+ const QList probes = this->getProbes();
+ int c = 0;
+ for (const CSimConnectObject &probe : probes)
+ {
+ this->remove(probe.getCallsign());
+ c++;
+ }
+ return c;
+ }
+
+ bool CSimConnectObjects::containsPendingAdded() const
+ {
+ for (const CSimConnectObject &simObject : *this)
+ {
+ if (simObject.isPendingAdded()) { return true; }
+ }
+ return false;
+ }
+
+ bool CSimConnectObjects::containsPendingRemoved() const
+ {
+ for (const CSimConnectObject &simObject : *this)
+ {
+ if (simObject.isPendingRemoved()) { return true; }
+ }
+ return false;
+ }
+
+ int CSimConnectObjects::countPendingAdded() const
+ {
+ int c = 0;
+ for (const CSimConnectObject &simObject : *this)
+ {
+ if (simObject.isPendingAdded()) { c++; }
+ }
+ return c;
+ }
+
+ int CSimConnectObjects::countPendingRemoved() const
+ {
+ int c = 0;
+ for (const CSimConnectObject &simObject : *this)
+ {
+ if (simObject.isPendingRemoved()) { c++; }
+ }
+ return c;
+ }
+
+ int CSimConnectObjects::countConfirmedAdded()
+ {
+ int c = 0;
+ for (const CSimConnectObject &simObject : std::as_const(*this))
+ {
+ if (simObject.isConfirmedAdded()) { c++; }
+ }
+ return c;
+ }
+
+ CCallsignSet CSimConnectObjects::getPendingAddedCallsigns() const
+ {
+ CCallsignSet callsigns;
+ for (const CSimConnectObject &simObject : *this)
+ {
+ if (simObject.isPendingAdded()) { callsigns.push_back(simObject.getCallsign()); }
+ }
+ return callsigns;
+ }
+
+ CCallsignSet CSimConnectObjects::getPendingRemovedCallsigns() const
+ {
+ CCallsignSet callsigns;
+ for (const CSimConnectObject &simObject : *this)
+ {
+ if (simObject.isPendingRemoved()) { callsigns.push_back(simObject.getCallsign()); }
+ }
+ return callsigns;
+ }
+
+ QList CSimConnectObjects::getByType(CSimConnectObject::SimObjectType type) const
+ {
+ QList objs;
+ for (const CSimConnectObject &simObject : *this)
+ {
+ if (simObject.getType() == type) { objs.push_back(simObject); }
+ }
+ return objs;
+ }
+
+ QList CSimConnectObjects::getAircraft() const
+ {
+ QList l = this->getByType(CSimConnectObject::AircraftNonAtc);
+ l.append(this->getByType(CSimConnectObject::AircraftSimulatedObject));
+ return l;
+ }
+
+ CSimConnectObject CSimConnectObjects::getNotPendingProbe() const
+ {
+ for (const CSimConnectObject &simObject : *this)
+ {
+ if (simObject.getType() == CSimConnectObject::TerrainProbe && !simObject.isPending()) { return simObject; }
+ }
+ return CSimConnectObject();
+ }
+
+ CSimConnectObject CSimConnectObjects::getOldestNotPendingProbe() const
+ {
+ CSimConnectObject oldestProbe;
+ for (const CSimConnectObject &simObject : *this)
+ {
+ if (simObject.getType() == CSimConnectObject::TerrainProbe && !simObject.isPending())
+ {
+ if (!oldestProbe.hasCreatedTimestamp() ||
+ oldestProbe.getCreatedTimestamp() > simObject.getCreatedTimestamp())
+ {
+ oldestProbe = simObject;
+ }
+ }
+ }
+ return oldestProbe;
+ }
+
+ bool CSimConnectObjects::containsType(CSimConnectObject::SimObjectType type) const
+ {
+ for (const CSimConnectObject &simObject : *this)
+ {
+ if (simObject.getType() == type) { return true; }
+ }
+ return false;
+ }
+
+ bool CSimConnectObjects::containsAircraft() const
+ {
+ return this->containsType(CSimConnectObject::AircraftNonAtc) ||
+ this->containsType(CSimConnectObject::AircraftSimulatedObject);
+ }
+
+ int CSimConnectObjects::removeCallsigns(const CCallsignSet &callsigns)
+ {
+ int c = 0;
+ for (const CCallsign &cs : callsigns) { c += this->remove(cs); }
+ return c;
+ }
+
+ CSimConnectObjects CSimConnectObjects::removeOutdatedPendingAdded(CSimConnectObject::SimObjectType type)
+ {
+ CCallsignSet removeCallsigns;
+ CSimConnectObjects removedObjects;
+
+ const qint64 ts = QDateTime::currentMSecsSinceEpoch();
+ for (const CSimConnectObject &simObject : std::as_const(*this))
+ {
+ // verification takes at least a second, so we need some time before outdating
+ if (type != CSimConnectObject::AllTypes && simObject.getType() != type) { continue; }
+ if (!simObject.isOutdatedPendingAdded(5000, ts)) { continue; }
+ removedObjects.insert(simObject);
+ removeCallsigns.insert(simObject.getCallsign());
+ }
+ if (!removeCallsigns.isEmpty()) { this->removeCallsigns(removeCallsigns); }
+ return removedObjects;
+ }
+} // namespace swift::simplugin::msfs2024common
diff --git a/src/plugins/simulator/msfs2024/simconnectobjectmsfs2024.h b/src/plugins/simulator/msfs2024/simconnectobjectmsfs2024.h
new file mode 100644
index 000000000..6843c57e7
--- /dev/null
+++ b/src/plugins/simulator/msfs2024/simconnectobjectmsfs2024.h
@@ -0,0 +1,450 @@
+// SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors
+// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
+
+//! \file
+
+#ifndef SWIFT_SIMPLUGIN_MSFS2024COMMON_SIMCONNECTOBJECT_H
+#define SWIFT_SIMPLUGIN_MSFS2024COMMON_SIMCONNECTOBJECT_H
+
+#include
+#include
+#include
+
+#include "misc/simulation/interpolation/interpolatormulti.h"
+#include "misc/simulation/simulatedaircraft.h"
+#include "plugins/simulator/msfs2024/msfs2024export.h"
+#include "plugins/simulator/msfs2024/simconnectdatadefinitionmsfs2024.h"
+#include "plugins/simulator/msfs2024/simconnectsymbolsmsfs2024.h"
+
+namespace swift::simplugin::msfs2024common
+{
+ //! Class representing a SimConnect object
+ class MSFS2024_EXPORT CSimConnectObject
+ {
+ public:
+ //! Type
+ enum SimObjectType
+ {
+ AircraftNonAtc,
+ AircraftSimulatedObject,
+ TerrainProbe,
+ AllTypes
+ };
+
+ //! Constructor
+ CSimConnectObject();
+
+ //! Constructor
+ CSimConnectObject(SimObjectType type);
+
+ //! Constructor providing initial situation/parts
+ CSimConnectObject(const swift::misc::simulation::CSimulatedAircraft &aircraft, DWORD requestId,
+ swift::misc::simulation::ISimulationEnvironmentProvider *simEnvProvider,
+ swift::misc::simulation::IInterpolationSetupProvider *setupProvider,
+ swift::misc::simulation::IRemoteAircraftProvider *remoteAircraftProvider,
+ swift::misc::simulation::CInterpolationLogger *logger);
+
+ //! Get callsign
+ const swift::misc::aviation::CCallsign &getCallsign() const { return m_aircraft.getCallsign(); }
+
+ //! Get callsign
+ const QString &getCallsignAsString() const { return m_aircraft.getCallsign().asString(); }
+
+ //! Callsign?
+ bool hasCallsign() const { return !this->getCallsign().isEmpty(); }
+
+ //! Simulated aircraft (as added)
+ const swift::misc::simulation::CSimulatedAircraft &getAircraft() const { return m_aircraft; }
+
+ //! Simulated aircraft model
+ const swift::misc::simulation::CAircraftModel &getAircraftModel() const { return m_aircraft.getModel(); }
+
+ //! Simulated aircraft model string
+ const QString &getAircraftModelString() const { return m_aircraft.getModelString(); }
+
+ //! Object type
+ SimObjectType getType() const { return m_type; }
+
+ //! Type as string
+ const QString &getTypeAsString() const { return typeToString(m_type); }
+
+ //! Aircraft?
+ bool isAircraft() const
+ {
+ return this->getType() == AircraftNonAtc || this->getType() == AircraftSimulatedObject;
+ }
+
+ //! Aircraft simulated object?
+ bool isAircraftSimulatedObject() const { return this->getType() == AircraftSimulatedObject; }
+
+ //! Aircraft NON ATC?
+ bool isAircraftNonAtc() const { return this->getType() == AircraftNonAtc; }
+
+ //! Probe?
+ bool isTerrainProbe() const { return this->getType() == TerrainProbe; }
+
+ //! Set the type
+ void setType(SimObjectType type) { m_type = type; }
+
+ //! Set the aircraft
+ void setAircraft(const swift::misc::simulation::CSimulatedAircraft &aircraft);
+
+ //! Set model string
+ void setAircraftModelString(const QString &modelString);
+
+ //! Set CG
+ void setAircraftCG(const swift::misc::physical_quantities::CLength &cg);
+
+ //! Get current lights (requested from simulator)
+ const swift::misc::aviation::CAircraftLights &getCurrentLightsInSimulator() const
+ {
+ return m_currentLightsInSim;
+ }
+
+ //! Received lights in simulator
+ bool hasCurrentLightsInSimulator() const { return !m_currentLightsInSim.isNull(); }
+
+ //! Set current lights when received from simulator
+ void setCurrentLightsInSimulator(const swift::misc::aviation::CAircraftLights &lights)
+ {
+ m_currentLightsInSim = lights;
+ }
+
+ //! Pretend to have received lights from simulator
+ void fakeCurrentLightsInSimulator() { m_currentLightsInSim.setNull(false); }
+
+ //! Lights as sent to simulator
+ const swift::misc::aviation::CAircraftLights &getLightsAsSent() const { return m_lightsAsSent; }
+
+ //! Lights as sent to simulator
+ void setLightsAsSent(const swift::misc::aviation::CAircraftLights &lights) { m_lightsAsSent = lights; }
+
+ //! How often do we request data from simulator for this remote aircraft
+ SIMCONNECT_PERIOD getSimDataPeriod() const { return m_requestSimDataPeriod; }
+
+ //! How often do we request data from simulator for this remote aircraft
+ void setSimDataPeriod(SIMCONNECT_PERIOD period) { m_requestSimDataPeriod = period; }
+
+ //! Set Simconnect request id
+ void setRequestId(DWORD id);
+
+ //! Get SimConnect request id
+ DWORD getRequestId() const { return m_requestId; }
+
+ //! Get SimConnect with offset
+ DWORD getRequestId(CSimConnectDefinitions::SimObjectRequest offset) const;
+
+ //! Set Simconnect object id
+ void setObjectId(DWORD id);
+
+ //! Get SimConnect object id
+ DWORD getObjectId() const { return m_objectId; }
+
+ //! Get SimConnect object id
+ QString getObjectIdAsString() const { return QString::number(this->getObjectId()); }
+
+ //! Valid request id?
+ bool hasValidRequestId() const { return m_validRequestId; }
+
+ //! Valid object id?
+ bool hasValidObjectId() const { return m_validObjectId; }
+
+ //! Object is requested in simulator, not yet confirmed added
+ bool isPendingAdded() const;
+
+ //! Still pending
+ bool isOutdatedPendingAdded(qint64 thresholdMs = 5000, qint64 currentMsSinceEpoch = -1) const;
+
+ //! Adding is confirmed
+ bool isConfirmedAdded() const;
+
+ //! Marked as confirmed, means the simulator has "confirmed" the objectId as added and not instantly removed the
+ //! object
+ void setConfirmedAdded(bool confirm);
+
+ //! @{
+ //! Special states
+ bool isAddedWhileRemoving() { return m_addedWhileRemoving; }
+ void setAddedWhileRemoving(bool addedWileRemoved);
+ bool isRemovedWhileAdding() const { return m_removedWhileAdding; }
+ void setRemovedWhileAdding(bool removedWhileAdding);
+ //! @}
+
+ //! Removing is pending
+ bool isPendingRemoved() const { return m_pendingRemoved; }
+
+ //! Object which can be used for sending, not pending and valid ids
+ bool isReadyToSend() const;
+
+ //! Marked as pending for removal
+ void setPendingRemoved(bool pending);
+
+ //! Pending added or removed?
+ bool isPending() const { return this->isPendingAdded() || this->isPendingRemoved(); }
+
+ //! Has camera?
+ bool hasCamera() const { return m_camera; }
+
+ //! Reset camera positions
+ void resetCameraPositions();
+
+ //! Camera position
+ const SIMCONNECT_DATA_XYZ &cameraPosition() const { return m_cameraPosition; }
+
+ //! Camera rotation;
+ const SIMCONNECT_DATA_PBH &cameraRotation() const { return m_cameraRotation; }
+
+ //! Camera position/rotation
+ void setCameraPositionAndRotation(const SIMCONNECT_DATA_XYZ &position, const SIMCONNECT_DATA_PBH &rotation)
+ {
+ m_cameraPosition = position;
+ m_cameraRotation = rotation;
+ }
+
+ //! Camera GUID
+ GUID getCameraGUID() const { return m_cameraGuid; }
+
+ //! Set camera GUID
+ void setCameraGUID(GUID guid)
+ {
+ m_cameraGuid = guid;
+ m_camera = true;
+ }
+
+ //! No camera anymore
+ void removeCamera() { m_camera = false; }
+
+ //! Set observer
+ void setObserverName(const QString &observer) { m_observerName = observer; }
+
+ //! Observer name
+ const QString &getObserverName() const { return m_observerName; }
+
+ //! Reset the state (like it was a new onject) without affecting interpolator and aircraft
+ void resetState();
+
+ //! Reset so it can be added again
+ void resetToAddAgain();
+
+ //! Reset the timestamp
+ void resetTimestampToNow() { m_tsCreated = QDateTime::currentMSecsSinceEpoch(); }
+
+ //! VTOL?
+ bool isVtol() const { return m_aircraft.isVtol(); }
+
+ //! Valid?
+ bool isValid() const { return !this->isInvalid(); }
+
+ //! Invalid?
+ bool isInvalid() const { return !this->hasValidObjectId() && !this->hasValidRequestId(); }
+
+ //! Created timestamp?
+ bool hasCreatedTimestamp() const { return m_tsCreated >= 0; }
+
+ //! Created timestamp
+ qint64 getCreatedTimestamp() const { return m_tsCreated; }
+
+ //! Engine count
+ int getEngineCount() const { return m_aircraft.getEnginesCount(); }
+
+ //! Was the object really added to simulator
+ bool hasValidRequestAndObjectId() const;
+
+ //! Adding has been failed before
+ int getAddingExceptions() const { return m_addingExceptions; }
+
+ //! Set adding failed before
+ void setAddingExceptions(int number) { m_addingExceptions = number; }
+
+ //! Increase adding exception
+ void increaseAddingExceptions() { m_addingExceptions++; }
+
+ //! Decrease adding exception
+ void decreaseAddingExceptions()
+ {
+ if (m_addingExceptions > 0) { m_addingExceptions--; }
+ }
+
+ //! Adding and directly removed
+ int getAddingDirectlyRemoved() const { return m_addingDirectlyRemoved; }
+
+ //! Set adding and directly removed
+ void setAddingDirectlyRemoved(int number) { m_addingDirectlyRemoved = number; }
+
+ //! Increase adding and directly removed
+ void increaseAddingDirectlyRemoved() { m_addingDirectlyRemoved++; }
+
+ //! Copy the counters from another object
+ void copyAddingFailureCounters(const CSimConnectObject &otherObject);
+
+ //! Callsign as LATIN1
+ const QByteArray &getCallsignByteArray() const { return m_callsignByteArray; }
+
+ //! \copydoc swift::misc::simulation::CInterpolator::getInterpolatorInfo
+ QString
+ getInterpolatorInfo(swift::misc::simulation::CInterpolationAndRenderingSetupBase::InterpolatorMode mode) const;
+
+ //! \copydoc swift::misc::simulation::CInterpolator::attachLogger
+ void attachInterpolatorLogger(swift::misc::simulation::CInterpolationLogger *logger) const;
+
+ //! \copydoc swift::misc::simulation::CInterpolator::getInterpolation
+ swift::misc::simulation::CInterpolationResult
+ getInterpolation(qint64 currentTimeSinceEpoch,
+ const swift::misc::simulation::CInterpolationAndRenderingSetupPerCallsign &setup,
+ uint32_t aircraftNumber) const;
+
+ //! \copydoc swift::misc::simulation::CInterpolator::getLastInterpolatedSituation
+ const swift::misc::aviation::CAircraftSituation &getLastInterpolatedSituation(
+ swift::misc::simulation::CInterpolationAndRenderingSetupBase::InterpolatorMode mode) const;
+
+ //! \copydoc swift::misc::simulation::CInterpolator::getInterpolationMessages
+ const swift::misc::CStatusMessageList &getInterpolationMessages(
+ swift::misc::simulation::CInterpolationAndRenderingSetupBase::InterpolatorMode mode) const;
+
+ //! Interpolator
+ swift::misc::simulation::CInterpolatorMulti *getInterpolator() const { return m_interpolator.data(); }
+
+ //! SimObject as string
+ QString toQString() const;
+
+ //! Type of id
+ static SimObjectType requestIdToType(DWORD requestId);
+
+ //! Type to string
+ static const QString &typeToString(SimObjectType type);
+
+ //! Same type
+ static bool isSameTypeGroup(SimObjectType t1, SimObjectType t2);
+
+ //! Aircraft?
+ static bool isAircraft(SimObjectType type);
+
+ private:
+ swift::misc::simulation::CSimulatedAircraft m_aircraft; //!< corresponding aircraft
+ SimObjectType m_type = AircraftNonAtc;
+ DWORD m_requestId = 0;
+ DWORD m_objectId = 0;
+ bool m_validRequestId = false;
+ bool m_validObjectId = false;
+ bool m_confirmedAdded = false;
+ bool m_pendingRemoved = false;
+ bool m_camera = false;
+ bool m_removedWhileAdding = false;
+ bool m_addedWhileRemoving = false;
+ int m_addingExceptions = 0; //!< exception when added
+ int m_addingDirectlyRemoved = 0; //!< added, but removed directly afterwards
+ qint64 m_tsCreated = -1;
+ GUID m_cameraGuid;
+ SIMCONNECT_DATA_XYZ m_cameraPosition;
+ SIMCONNECT_DATA_PBH m_cameraRotation;
+ QByteArray m_callsignByteArray;
+ QString m_observerName;
+ swift::misc::aviation::CAircraftLights m_currentLightsInSim {
+ nullptr
+ }; //!< current lights to know state for toggling
+ swift::misc::aviation::CAircraftLights m_lightsAsSent { nullptr }; //!< lights as sent to simulator
+ SIMCONNECT_PERIOD m_requestSimDataPeriod = SIMCONNECT_PERIOD_NEVER; //!< how often do we query ground elevation
+ QSharedPointer
+ m_interpolator; //!< shared pointer because CSimConnectObject can be copied
+ };
+
+ //! Simulator objects (aka AI aircraft)
+ class CSimConnectObjects : public QHash
+ {
+ public:
+ //! Insert
+ bool insert(const CSimConnectObject &simObject, bool updateTimestamp = false);
+
+ //! Set ID of a SimConnect object, so far we only have an request id in the object
+ bool setSimConnectObjectIdForRequestId(DWORD requestId, DWORD objectId);
+
+ //! Find which callsign belongs to the object id
+ swift::misc::aviation::CCallsign getCallsignForObjectId(DWORD objectId) const;
+
+ //! Get object per object id
+ CSimConnectObject getSimObjectForObjectId(DWORD objectId) const;
+
+ //! Get object per request id
+ CSimConnectObject getSimObjectForRequestId(DWORD requestId) const;
+
+ //! Get by request or object id, just as possible
+ CSimConnectObject getSimObjectForOtherSimObject(const CSimConnectObject &otherSimObj) const;
+
+ //! Get the oldest object
+ CSimConnectObject getOldestObject() const;
+
+ //! Is the object id one of our AI objects?
+ bool isKnownSimObjectId(DWORD objectId) const;
+
+ //! Remove by id
+ bool removeByObjectId(DWORD objectId);
+
+ //! Remove by object id or request id
+ bool removeByOtherSimObject(const CSimConnectObject &otherSimObj);
+
+ //! Remove all the probes
+ int removeAllProbes();
+
+ //! Remove callsigns
+ int removeCallsigns(const swift::misc::aviation::CCallsignSet &callsigns);
+
+ //! Remove all pending added objects
+ CSimConnectObjects removeOutdatedPendingAdded(CSimConnectObject::SimObjectType type);
+
+ //! Pending add condition
+ bool containsPendingAdded() const;
+
+ //! Pending removed condition
+ bool containsPendingRemoved() const;
+
+ //! Number of pending added
+ int countPendingAdded() const;
+
+ //! Number of pending removed
+ int countPendingRemoved() const;
+
+ //! Objects not pending
+ int countConfirmedAdded();
+
+ //! Get all callsigns
+ swift::misc::aviation::CCallsignSet getAllCallsigns(bool withoutProbes = true) const;
+
+ //! Get all callsign strings
+ QStringList getAllCallsignStrings(bool sorted = false, bool withoutProbes = true) const;
+
+ //! Get all callsign strings as string
+ QString getAllCallsignStringsAsString(bool sorted = false, const QString &separator = ", ") const;
+
+ //! Callsigns of pending added callsigns
+ swift::misc::aviation::CCallsignSet getPendingAddedCallsigns() const;
+
+ //! Callsigns of pending removed callsigns
+ swift::misc::aviation::CCallsignSet getPendingRemovedCallsigns() const;
+
+ //! Get by type
+ QList getByType(CSimConnectObject::SimObjectType type) const;
+
+ //! All probes
+ QList getProbes() const { return this->getByType(CSimConnectObject::TerrainProbe); }
+
+ //! All aircraft
+ QList getAircraft() const;
+
+ //! Get a non pending probe
+ CSimConnectObject getNotPendingProbe() const;
+
+ //! Get a non pending probe
+ CSimConnectObject getOldestNotPendingProbe() const;
+
+ //! Contains object of type
+ bool containsType(CSimConnectObject::SimObjectType type) const;
+
+ //! Probe?
+ bool containsProbe() const { return this->containsType(CSimConnectObject::TerrainProbe); }
+
+ //! Aircraft?
+ bool containsAircraft() const;
+ };
+} // namespace swift::simplugin::msfs2024common
+
+#endif // SWIFT_SIMPLUGIN_MSFS2024COMMON_SIMCONNECTOBJECT_H
diff --git a/src/plugins/simulator/msfs2024/simconnectsymbolsmsfs2024.cpp b/src/plugins/simulator/msfs2024/simconnectsymbolsmsfs2024.cpp
new file mode 100644
index 000000000..0396cebdb
--- /dev/null
+++ b/src/plugins/simulator/msfs2024/simconnectsymbolsmsfs2024.cpp
@@ -0,0 +1,585 @@
+// SPDX-FileCopyrightText: Copyright (C) 2018 swift Project Community / Contributors
+// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
+
+#include "simconnectsymbolsmsfs2024.h"
+
+// clang-format off
+#include
+//#include
+#include "../third_party/externals/common/include/simconnect/MSFS2024/SimConnect.h"
+// clang-format on
+
+#include
+
+#include
+
+#include "misc/logcategories.h"
+#include "misc/logmessage.h"
+#include "misc/stringutils.h"
+
+// clazy:excludeall=function-args-by-ref
+
+using namespace swift::misc;
+
+bool loadAndResolveSimConnect(bool manifestProbing)
+{
+ Q_UNUSED(manifestProbing);
+ return true;
+}
+
+// MSFS2024 API:
+// https://docs.flightsimulator.com/msfs2024/html/6_Programming_APIs/SimConnect/SimConnect_API_Reference.htm
+using PfnSimConnect_Open = HRESULT(__stdcall *)(HANDLE *, LPCSTR, HWND, DWORD, HANDLE, DWORD);
+using PfnSimConnect_Close = HRESULT(__stdcall *)(HANDLE);
+using PfnSimConnect_AddToDataDefinition = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_DATA_DEFINITION_ID, const char *,
+ const char *, SIMCONNECT_DATATYPE, float, DWORD);
+using PfnSimConnect_Text = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_TEXT_TYPE, float, SIMCONNECT_CLIENT_EVENT_ID, DWORD,
+ void *);
+using PfnSimConnect_CallDispatch = HRESULT(__stdcall *)(HANDLE, DispatchProc, void *);
+using PfnSimConnect_WeatherSetModeCustom = HRESULT(__stdcall *)(HANDLE);
+using PfnSimConnect_WeatherSetModeGlobal = HRESULT(__stdcall *)(HANDLE);
+using PfnSimConnect_WeatherSetObservation = HRESULT(__stdcall *)(HANDLE, DWORD, const char *);
+using PfnSimConnect_TransmitClientEvent = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_OBJECT_ID, SIMCONNECT_CLIENT_EVENT_ID,
+ DWORD, SIMCONNECT_NOTIFICATION_GROUP_ID,
+ SIMCONNECT_EVENT_FLAG);
+using PfnSimConnect_SetClientData = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_CLIENT_DATA_ID,
+ SIMCONNECT_CLIENT_DATA_DEFINITION_ID,
+ SIMCONNECT_CLIENT_DATA_SET_FLAG, DWORD, DWORD, void *);
+using PfnSimConnect_RequestDataOnSimObject = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_DATA_REQUEST_ID,
+ SIMCONNECT_DATA_DEFINITION_ID, SIMCONNECT_OBJECT_ID,
+ SIMCONNECT_PERIOD, SIMCONNECT_DATA_REQUEST_FLAG,
+ DWORD, DWORD, DWORD);
+using PfnSimConnect_RequestDataOnSimObjectType = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_DATA_REQUEST_ID,
+ SIMCONNECT_DATA_DEFINITION_ID, DWORD,
+ SIMCONNECT_SIMOBJECT_TYPE);
+using PfnSimConnect_RequestClientData = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_CLIENT_DATA_ID,
+ SIMCONNECT_DATA_REQUEST_ID,
+ SIMCONNECT_CLIENT_DATA_DEFINITION_ID,
+ SIMCONNECT_CLIENT_DATA_PERIOD,
+ SIMCONNECT_CLIENT_DATA_REQUEST_FLAG, DWORD, DWORD, DWORD);
+using PfnSimConnect_SubscribeToSystemEvent = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_CLIENT_EVENT_ID, const char *);
+using PfnSimConnect_MapClientEventToSimEvent = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_CLIENT_EVENT_ID, const char *);
+using PfnSimConnect_SubscribeToFacilities = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_FACILITY_LIST_TYPE,
+ SIMCONNECT_DATA_REQUEST_ID);
+using PfnSimConnect_GetLastSentPacketID = HRESULT(__stdcall *)(HANDLE, DWORD *);
+using PfnSimConnect_AIRemoveObject = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_OBJECT_ID, SIMCONNECT_DATA_REQUEST_ID);
+using PfnSimConnect_SetDataOnSimObject = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_DATA_DEFINITION_ID,
+ SIMCONNECT_OBJECT_ID, SIMCONNECT_DATA_SET_FLAG, DWORD,
+ DWORD, void *);
+using PfnSimConnect_AIReleaseControl = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_OBJECT_ID, SIMCONNECT_DATA_REQUEST_ID);
+using PfnSimConnect_AICreateNonATCAircraft = HRESULT(__stdcall *)(HANDLE, const char *, const char *,
+ SIMCONNECT_DATA_INITPOSITION,
+ SIMCONNECT_DATA_REQUEST_ID);
+using PfnSimConnect_AICreateNonATCAircraft_EX1 = HRESULT(__stdcall *)(HANDLE, const char *, const char *, const char *,
+ SIMCONNECT_DATA_INITPOSITION,
+ SIMCONNECT_DATA_REQUEST_ID);
+
+using PfnSimConnect_AICreateEnrouteATCAircraft = HRESULT(__stdcall *)(HANDLE, const char *, const char *, int,
+ const char *, double, BOOL,
+ SIMCONNECT_DATA_REQUEST_ID);
+using PfnSimConnect_AICreateParkedATCAircraft = HRESULT(__stdcall *)(HANDLE, const char *, const char *, const char *,
+ SIMCONNECT_DATA_REQUEST_ID);
+using PfnSimConnect_AICreateSimulatedObject = HRESULT(__stdcall *)(HANDLE, const char *, SIMCONNECT_DATA_INITPOSITION,
+ SIMCONNECT_DATA_REQUEST_ID);
+
+using PfnSimConnect_MapClientDataNameToID = HRESULT(__stdcall *)(HANDLE, const char *, SIMCONNECT_CLIENT_DATA_ID);
+using PfnSimConnect_CreateClientData = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_CLIENT_DATA_ID, DWORD,
+ SIMCONNECT_CREATE_CLIENT_DATA_FLAG);
+using PfnSimConnect_AddToClientDataDefinition = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_CLIENT_DATA_DEFINITION_ID,
+ DWORD, DWORD, float, DWORD);
+
+#ifdef Q_OS_WIN64
+// using PfnSimConnect_RequestGroundInfo = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_DATA_REQUEST_ID, double, double,
+// double,
+// double, double, double, DWORD, DWORD,
+// SIMCONNECT_GROUND_INFO_LATLON_FORMAT,
+// SIMCONNECT_GROUND_INFO_ALT_FORMAT,
+// SIMCONNECT_GROUND_INFO_SOURCE_FLAG);
+using PfnSimConnect_ChangeView = HRESULT(__stdcall *)(HANDLE, const char *);
+using PfnSimConnect_AIReleaseControlEx = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_OBJECT_ID, SIMCONNECT_DATA_REQUEST_ID,
+ BOOL);
+using PfnSimConnect_CloseView = HRESULT(__stdcall *)(HANDLE, const char *);
+using PfnSimConnect_OpenView = HRESULT(__stdcall *)(HANDLE, const char *, const char *);
+using PfnSimConnect_ChangeView = HRESULT(__stdcall *)(HANDLE, const char *);
+using PfnSimConnect_CreateCameraInstance = HRESULT(__stdcall *)(HANDLE, const GUID, const char *, SIMCONNECT_OBJECT_ID,
+ SIMCONNECT_DATA_REQUEST_ID);
+using PfnSimConnect_DeleteCameraInstance = HRESULT(__stdcall *)(HANDLE, const GUID, UINT32);
+// using PfnSimConnect_CreateCameraDefinition = HRESULT(__stdcall *)(HANDLE, const GUID, SIMCONNECT_CAMERA_TYPE,
+// const char *, SIMCONNECT_DATA_XYZ,
+// SIMCONNECT_DATA_PBH);
+using PfnSimConnect_ObserverAttachToEntityOn = HRESULT(__stdcall *)(HANDLE, const char *, DWORD, SIMCONNECT_DATA_XYZ);
+// using PfnSimConnect_CreateObserver = HRESULT(__stdcall *)(HANDLE, const char *, SIMCONNECT_DATA_OBSERVER);
+using PfnSimConnect_SetObserverLookAt = HRESULT(__stdcall *)(HANDLE, const char *, SIMCONNECT_DATA_LATLONALT);
+using PfnSimConnect_SimConnect_EnumerateSimObjectsAndLiveries = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_DATA_REQUEST_ID,
+ SIMCONNECT_SIMOBJECT_TYPE);
+using PfnSimConnect_AICreateSimulatedObject_EX1 = HRESULT(__stdcall *)(HANDLE, const char *, const char *,
+ SIMCONNECT_DATA_INITPOSITION,
+ SIMCONNECT_DATA_REQUEST_ID);
+
+#endif
+
+//! The SimConnect Symbols
+//! \private
+struct SimConnectSymbols
+{
+ PfnSimConnect_Open SimConnect_Open = nullptr;
+ PfnSimConnect_Close SimConnect_Close = nullptr;
+ PfnSimConnect_AddToDataDefinition SimConnect_AddToDataDefinition = nullptr;
+ PfnSimConnect_Text SimConnect_Text = nullptr;
+ PfnSimConnect_CallDispatch SimConnect_CallDispatch = nullptr;
+ PfnSimConnect_WeatherSetModeCustom SimConnect_WeatherSetModeCustom = nullptr;
+ PfnSimConnect_WeatherSetModeGlobal SimConnect_WeatherSetModeGlobal = nullptr;
+ PfnSimConnect_WeatherSetObservation SimConnect_WeatherSetObservation = nullptr;
+ PfnSimConnect_TransmitClientEvent SimConnect_TransmitClientEvent = nullptr;
+ PfnSimConnect_SetClientData SimConnect_SetClientData = nullptr;
+ PfnSimConnect_RequestDataOnSimObject SimConnect_RequestDataOnSimObject = nullptr;
+ PfnSimConnect_RequestDataOnSimObjectType SimConnect_RequestDataOnSimObjectType = nullptr;
+ PfnSimConnect_RequestClientData SimConnect_RequestClientData = nullptr;
+ PfnSimConnect_SubscribeToSystemEvent SimConnect_SubscribeToSystemEvent = nullptr;
+ PfnSimConnect_MapClientEventToSimEvent SimConnect_MapClientEventToSimEvent = nullptr;
+ PfnSimConnect_SubscribeToFacilities SimConnect_SubscribeToFacilities = nullptr;
+ PfnSimConnect_GetLastSentPacketID SimConnect_GetLastSentPacketID = nullptr;
+ PfnSimConnect_AIRemoveObject SimConnect_AIRemoveObject = nullptr;
+ PfnSimConnect_SetDataOnSimObject SimConnect_SetDataOnSimObject = nullptr;
+ PfnSimConnect_AIReleaseControl SimConnect_AIReleaseControl = nullptr;
+ PfnSimConnect_AICreateNonATCAircraft SimConnect_AICreateNonATCAircraft = nullptr;
+
+ PfnSimConnect_AICreateParkedATCAircraft SimConnect_AICreateParkedATCAircraft = nullptr;
+ PfnSimConnect_AICreateEnrouteATCAircraft SimConnect_AICreateEnrouteATCAircraft = nullptr;
+ PfnSimConnect_AICreateSimulatedObject SimConnect_AICreateSimulatedObject = nullptr;
+
+ PfnSimConnect_MapClientDataNameToID SimConnect_MapClientDataNameToID = nullptr;
+ PfnSimConnect_CreateClientData SimConnect_CreateClientData = nullptr;
+ PfnSimConnect_AddToClientDataDefinition SimConnect_AddToClientDataDefinition = nullptr;
+#ifdef Q_OS_WIN64
+ // PfnSimConnect_RequestGroundInfo SimConnect_RequestGroundInfo = nullptr;
+ PfnSimConnect_ChangeView SimConnect_ChangeView = nullptr;
+ PfnSimConnect_AIReleaseControlEx SimConnect_AIReleaseControlEx = nullptr;
+ // PfnSimConnect_CreateCameraDefinition SimConnect_CreateCameraDefinition = nullptr;
+ PfnSimConnect_ObserverAttachToEntityOn SimConnect_ObserverAttachToEntityOn = nullptr;
+ // PfnSimConnect_CreateObserver SimConnect_CreateObserver = nullptr;
+ PfnSimConnect_CreateCameraInstance SimConnect_CreateCameraInstance = nullptr;
+ PfnSimConnect_DeleteCameraInstance SimConnect_DeleteCameraInstance = nullptr;
+ PfnSimConnect_SetObserverLookAt SimConnect_SetObserverLookAt = nullptr;
+ PfnSimConnect_OpenView SimConnect_OpenView = nullptr;
+ PfnSimConnect_CloseView SimConnect_CloseView = nullptr;
+ PfnSimConnect_SimConnect_EnumerateSimObjectsAndLiveries SimConnect_EnumerateSimObjectsAndLiveries = nullptr;
+ PfnSimConnect_AICreateNonATCAircraft_EX1 SimConnect_AICreateNonATCAircraft_EX1 = nullptr;
+ PfnSimConnect_AICreateSimulatedObject_EX1 SimConnect_AICreateSimulatedObject_EX1 = nullptr;
+#endif
+};
+
+static SimConnectSymbols gSymbols;
+
+template
+bool resolveSimConnectSymbol(QLibrary &library, FuncPtr &funcPtr, const char *funcName)
+{
+ funcPtr = reinterpret_cast(library.resolve(funcName));
+ if (!funcPtr)
+ {
+ CLogMessage(CLogCategories::driver()).error(u"Failed to resolve %1: %2") << funcName << library.errorString();
+ return false;
+ }
+ return true;
+}
+
+bool resolveMsfs2024SimConnectSymbols(QLibrary &simConnectDll)
+{
+ bool resolveSuccess = true;
+ resolveSuccess =
+ resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_Open, "SimConnect_Open");
+ resolveSuccess =
+ resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_Close, "SimConnect_Close");
+ resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_AddToDataDefinition,
+ "SimConnect_AddToDataDefinition");
+ resolveSuccess =
+ resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_Text, "SimConnect_Text");
+ resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_CallDispatch,
+ "SimConnect_CallDispatch");
+ resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_WeatherSetModeCustom,
+ "SimConnect_WeatherSetModeCustom");
+ resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_WeatherSetModeGlobal,
+ "SimConnect_WeatherSetModeGlobal");
+ resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_WeatherSetObservation,
+ "SimConnect_WeatherSetObservation");
+ resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_TransmitClientEvent,
+ "SimConnect_TransmitClientEvent");
+ resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_SetClientData,
+ "SimConnect_SetClientData");
+ resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_RequestDataOnSimObject,
+ "SimConnect_RequestDataOnSimObject");
+ resolveSuccess =
+ resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_RequestDataOnSimObjectType,
+ "SimConnect_RequestDataOnSimObjectType");
+ resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_RequestClientData,
+ "SimConnect_RequestClientData");
+ resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_SubscribeToSystemEvent,
+ "SimConnect_SubscribeToSystemEvent");
+ resolveSuccess =
+ resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_MapClientEventToSimEvent,
+ "SimConnect_MapClientEventToSimEvent");
+ resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_SubscribeToFacilities,
+ "SimConnect_SubscribeToFacilities");
+ resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_GetLastSentPacketID,
+ "SimConnect_GetLastSentPacketID");
+ resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_AIRemoveObject,
+ "SimConnect_AIRemoveObject");
+ resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_SetDataOnSimObject,
+ "SimConnect_SetDataOnSimObject");
+ resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_AIReleaseControl,
+ "SimConnect_AIReleaseControl");
+ resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_AICreateNonATCAircraft,
+ "SimConnect_AICreateNonATCAircraft");
+ resolveSuccess =
+ resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_AICreateNonATCAircraft_EX1,
+ "SimConnect_AICreateNonATCAircraft_EX1");
+ resolveSuccess =
+ resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_AICreateEnrouteATCAircraft,
+ "SimConnect_AICreateEnrouteATCAircraft");
+ resolveSuccess =
+ resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_AICreateParkedATCAircraft,
+ "SimConnect_AICreateParkedATCAircraft");
+ resolveSuccess =
+ resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_AICreateSimulatedObject,
+ "SimConnect_AICreateSimulatedObject");
+ resolveSuccess =
+ resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_AICreateSimulatedObject_EX1,
+ "SimConnect_AICreateSimulatedObject_EX1");
+ resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_MapClientDataNameToID,
+ "SimConnect_MapClientDataNameToID");
+ resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_CreateClientData,
+ "SimConnect_CreateClientData");
+ resolveSuccess =
+ resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_AddToClientDataDefinition,
+ "SimConnect_AddToClientDataDefinition");
+
+ resolveSuccess =
+ resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_EnumerateSimObjectsAndLiveries,
+ "SimConnect_EnumerateSimObjectsAndLiveries");
+
+ return resolveSuccess;
+}
+
+#ifdef Q_OS_WIN64
+// bool resolveP3DSimConnectSymbols(QLibrary &simConnectDll)
+//{
+// bool resolveSuccess = true;
+// resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_RequestGroundInfo,
+// "SimConnect_RequestGroundInfo");
+// resolveSuccess = resolveSuccess &
+// resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_ChangeView, "SimConnect_ChangeView");
+// resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_AIReleaseControlEx,
+// "SimConnect_AIReleaseControlEx");
+// resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll,
+// gSymbols.SimConnect_CreateCameraDefinition,
+// "SimConnect_CreateCameraDefinition");
+// resolveSuccess =
+// resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_ObserverAttachToEntityOn,
+// "SimConnect_ObserverAttachToEntityOn");
+// resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_CreateObserver,
+// "SimConnect_CreateObserver");
+// resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll,
+// gSymbols.SimConnect_CreateCameraInstance,
+// "SimConnect_CreateCameraInstance");
+// resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll,
+// gSymbols.SimConnect_DeleteCameraInstance,
+// "SimConnect_DeleteCameraInstance");
+// resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_SetObserverLookAt,
+// "SimConnect_SetObserverLookAt");
+// resolveSuccess =
+// resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_OpenView, "SimConnect_OpenView");
+// resolveSuccess =
+// resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_CloseView,
+// "SimConnect_CloseView");
+// return resolveSuccess;
+// }
+
+bool loadAndResolveMSFS2024SimConnect()
+{
+ // Check if already loaded
+ if (gSymbols.SimConnect_Open) { return true; }
+
+ QString simConnectFileName(QStringLiteral("SimConnect.MSFS2024"));
+
+ QLibrary simConnectDll(simConnectFileName);
+ simConnectDll.setLoadHints(QLibrary::PreventUnloadHint);
+ if (simConnectDll.load())
+ {
+ const bool resolvedCommon = resolveMsfs2024SimConnectSymbols(simConnectDll);
+ if (!resolvedCommon)
+ {
+ CLogMessage(CLogCategories::driver()).error(u"Failed to resolve common symbols from SimConnect.dll: '%1'")
+ << simConnectFileName;
+ return false;
+ }
+
+ CLogMessage(CLogCategories::driver()).info(u"Loaded and resolved MSFS2024 symbols from SimConnect.dll: '%1'")
+ << simConnectFileName;
+ return resolvedCommon;
+ }
+ else
+ {
+ CLogMessage(CLogCategories::driver()).error(u"Failed to load SimConnect.dll: '%1' '%2'")
+ << simConnectFileName << simConnectDll.errorString();
+ return false;
+ }
+}
+
+#endif
+
+SIMCONNECTAPI SimConnect_Open(HANDLE *phSimConnect, LPCSTR szName, HWND hWnd, DWORD UserEventWin32, HANDLE hEventHandle,
+ DWORD ConfigIndex)
+{
+ return gSymbols.SimConnect_Open(phSimConnect, szName, hWnd, UserEventWin32, hEventHandle, ConfigIndex);
+}
+
+SIMCONNECTAPI SimConnect_Close(HANDLE hSimConnect) { return gSymbols.SimConnect_Close(hSimConnect); }
+
+SIMCONNECTAPI SimConnect_AddToDataDefinition(HANDLE hSimConnect, SIMCONNECT_DATA_DEFINITION_ID DefineID,
+ const char *DatumName, const char *UnitsName,
+ SIMCONNECT_DATATYPE DatumType, float fEpsilon, DWORD DatumID)
+{
+ return gSymbols.SimConnect_AddToDataDefinition(hSimConnect, DefineID, DatumName, UnitsName, DatumType, fEpsilon,
+ DatumID);
+}
+
+SIMCONNECTAPI SimConnect_EnumerateSimObjectsAndLiveries(HANDLE hSimConnect, SIMCONNECT_DATA_REQUEST_ID RequestID,
+ SIMCONNECT_SIMOBJECT_TYPE Type)
+{
+ return gSymbols.SimConnect_EnumerateSimObjectsAndLiveries(hSimConnect, RequestID, Type);
+}
+
+SIMCONNECTAPI SimConnect_Text(HANDLE hSimConnect, SIMCONNECT_TEXT_TYPE type, float fTimeSeconds,
+ SIMCONNECT_CLIENT_EVENT_ID EventID, DWORD cbUnitSize, void *pDataSet)
+{
+ return gSymbols.SimConnect_Text(hSimConnect, type, fTimeSeconds, EventID, cbUnitSize, pDataSet);
+}
+
+SIMCONNECTAPI SimConnect_CallDispatch(HANDLE hSimConnect, DispatchProc pfcnDispatch, void *pContext)
+{
+ return gSymbols.SimConnect_CallDispatch(hSimConnect, pfcnDispatch, pContext);
+}
+
+SIMCONNECTAPI SimConnect_WeatherSetModeCustom(HANDLE hSimConnect)
+{
+ return gSymbols.SimConnect_WeatherSetModeCustom(hSimConnect);
+}
+
+SIMCONNECTAPI SimConnect_WeatherSetModeGlobal(HANDLE hSimConnect)
+{
+ return gSymbols.SimConnect_WeatherSetModeGlobal(hSimConnect);
+}
+
+SIMCONNECTAPI SimConnect_WeatherSetObservation(HANDLE hSimConnect, DWORD Seconds, const char *szMETAR)
+{
+ return gSymbols.SimConnect_WeatherSetObservation(hSimConnect, Seconds, szMETAR);
+}
+
+SIMCONNECTAPI SimConnect_TransmitClientEvent(HANDLE hSimConnect, SIMCONNECT_OBJECT_ID ObjectID,
+ SIMCONNECT_CLIENT_EVENT_ID EventID, DWORD dwData,
+ SIMCONNECT_NOTIFICATION_GROUP_ID GroupID, SIMCONNECT_EVENT_FLAG Flags)
+{
+ return gSymbols.SimConnect_TransmitClientEvent(hSimConnect, ObjectID, EventID, dwData, GroupID, Flags);
+}
+
+SIMCONNECTAPI SimConnect_SetClientData(HANDLE hSimConnect, SIMCONNECT_CLIENT_DATA_ID ClientDataID,
+ SIMCONNECT_CLIENT_DATA_DEFINITION_ID DefineID,
+ SIMCONNECT_CLIENT_DATA_SET_FLAG Flags, DWORD dwReserved, DWORD cbUnitSize,
+ void *pDataSet)
+{
+ return gSymbols.SimConnect_SetClientData(hSimConnect, ClientDataID, DefineID, Flags, dwReserved, cbUnitSize,
+ pDataSet);
+}
+
+SIMCONNECTAPI SimConnect_RequestDataOnSimObject(HANDLE hSimConnect, SIMCONNECT_DATA_REQUEST_ID RequestID,
+ SIMCONNECT_DATA_DEFINITION_ID DefineID, SIMCONNECT_OBJECT_ID ObjectID,
+ SIMCONNECT_PERIOD Period, SIMCONNECT_DATA_REQUEST_FLAG Flags,
+ DWORD origin, DWORD interval, DWORD limit)
+{
+ return gSymbols.SimConnect_RequestDataOnSimObject(hSimConnect, RequestID, DefineID, ObjectID, Period, Flags, origin,
+ interval, limit);
+}
+
+SIMCONNECTAPI SimConnect_RequestDataOnSimObjectType(HANDLE hSimConnect, SIMCONNECT_DATA_REQUEST_ID RequestID,
+ SIMCONNECT_DATA_DEFINITION_ID DefineID, DWORD dwRadiusMeters,
+ SIMCONNECT_SIMOBJECT_TYPE type)
+{
+ return gSymbols.SimConnect_RequestDataOnSimObjectType(hSimConnect, RequestID, DefineID, dwRadiusMeters, type);
+}
+
+SIMCONNECTAPI SimConnect_RequestClientData(HANDLE hSimConnect, SIMCONNECT_CLIENT_DATA_ID ClientDataID,
+ SIMCONNECT_DATA_REQUEST_ID RequestID,
+ SIMCONNECT_CLIENT_DATA_DEFINITION_ID DefineID,
+ SIMCONNECT_CLIENT_DATA_PERIOD Period,
+ SIMCONNECT_CLIENT_DATA_REQUEST_FLAG Flags, DWORD origin, DWORD interval,
+ DWORD limit)
+{
+ return gSymbols.SimConnect_RequestClientData(hSimConnect, ClientDataID, RequestID, DefineID, Period, Flags, origin,
+ interval, limit);
+}
+
+SIMCONNECTAPI SimConnect_SubscribeToSystemEvent(HANDLE hSimConnect, SIMCONNECT_CLIENT_EVENT_ID EventID,
+ const char *SystemEventName)
+{
+ return gSymbols.SimConnect_SubscribeToSystemEvent(hSimConnect, EventID, SystemEventName);
+}
+
+SIMCONNECTAPI SimConnect_MapClientEventToSimEvent(HANDLE hSimConnect, SIMCONNECT_CLIENT_EVENT_ID EventID,
+ const char *EventName)
+{
+ return gSymbols.SimConnect_MapClientEventToSimEvent(hSimConnect, EventID, EventName);
+}
+
+SIMCONNECTAPI SimConnect_SubscribeToFacilities(HANDLE hSimConnect, SIMCONNECT_FACILITY_LIST_TYPE type,
+ SIMCONNECT_DATA_REQUEST_ID RequestID)
+{
+ return gSymbols.SimConnect_SubscribeToFacilities(hSimConnect, type, RequestID);
+}
+
+SIMCONNECTAPI SimConnect_GetLastSentPacketID(HANDLE hSimConnect, DWORD *pdwError)
+{
+ return gSymbols.SimConnect_GetLastSentPacketID(hSimConnect, pdwError);
+}
+
+SIMCONNECTAPI SimConnect_AIRemoveObject(HANDLE hSimConnect, SIMCONNECT_OBJECT_ID ObjectID,
+ SIMCONNECT_DATA_REQUEST_ID RequestID)
+{
+ return gSymbols.SimConnect_AIRemoveObject(hSimConnect, ObjectID, RequestID);
+}
+
+SIMCONNECTAPI SimConnect_SetDataOnSimObject(HANDLE hSimConnect, SIMCONNECT_DATA_DEFINITION_ID DefineID,
+ SIMCONNECT_OBJECT_ID ObjectID, SIMCONNECT_DATA_SET_FLAG Flags,
+ DWORD ArrayCount, DWORD cbUnitSize, void *pDataSet)
+{
+ return gSymbols.SimConnect_SetDataOnSimObject(hSimConnect, DefineID, ObjectID, Flags, ArrayCount, cbUnitSize,
+ pDataSet);
+}
+
+SIMCONNECTAPI SimConnect_AIReleaseControl(HANDLE hSimConnect, SIMCONNECT_OBJECT_ID ObjectID,
+ SIMCONNECT_DATA_REQUEST_ID RequestID)
+{
+ return gSymbols.SimConnect_AIReleaseControl(hSimConnect, ObjectID, RequestID);
+}
+
+SIMCONNECTAPI SimConnect_AICreateNonATCAircraft(HANDLE hSimConnect, const char *szContainerTitle,
+ const char *szTailNumber, SIMCONNECT_DATA_INITPOSITION InitPos,
+ SIMCONNECT_DATA_REQUEST_ID RequestID)
+{
+ return gSymbols.SimConnect_AICreateNonATCAircraft(hSimConnect, szContainerTitle, szTailNumber, InitPos, RequestID);
+}
+
+SIMCONNECTAPI SimConnect_AICreateNonATCAircraft_EX1(HANDLE hSimConnect, const char *szContainerTitle,
+ const char *szContainerLivery, const char *szTailNumber,
+ SIMCONNECT_DATA_INITPOSITION InitPos,
+ SIMCONNECT_DATA_REQUEST_ID RequestID)
+{
+ return gSymbols.SimConnect_AICreateNonATCAircraft_EX1(hSimConnect, szContainerTitle, szContainerLivery,
+ szTailNumber, InitPos, RequestID);
+}
+
+SIMCONNECTAPI SimConnect_AICreateEnrouteATCAircraft(HANDLE hSimConnect, const char *szContainerTitle,
+ const char *szTailNumber, int iFlightNumber,
+ const char *szFlightPlanPath, double dFlightPlanPosition,
+ BOOL bTouchAndGo, SIMCONNECT_DATA_REQUEST_ID RequestID)
+{
+ return gSymbols.SimConnect_AICreateEnrouteATCAircraft(hSimConnect, szContainerTitle, szTailNumber, iFlightNumber,
+ szFlightPlanPath, dFlightPlanPosition, bTouchAndGo,
+ RequestID);
+}
+
+SIMCONNECTAPI SimConnect_AICreateParkedATCAircraft(HANDLE hSimConnect, const char *szContainerTitle,
+ const char *szTailNumber, const char *szAirportID,
+ SIMCONNECT_DATA_REQUEST_ID RequestID)
+{
+ return gSymbols.SimConnect_AICreateParkedATCAircraft(hSimConnect, szContainerTitle, szTailNumber, szAirportID,
+ RequestID);
+}
+
+SIMCONNECTAPI SimConnect_AICreateSimulatedObject_EX1(HANDLE hSimConnect, const char *szContainerTitle,
+ const char *szContainerLivery,
+ SIMCONNECT_DATA_INITPOSITION InitPos,
+ SIMCONNECT_DATA_REQUEST_ID RequestID)
+{
+ return gSymbols.SimConnect_AICreateSimulatedObject_EX1(hSimConnect, szContainerTitle, szContainerLivery, InitPos,
+ RequestID);
+}
+
+SIMCONNECTAPI SimConnect_AICreateSimulatedObject(HANDLE hSimConnect, const char *szContainerTitle,
+ SIMCONNECT_DATA_INITPOSITION InitPos,
+ SIMCONNECT_DATA_REQUEST_ID RequestID)
+{
+ return gSymbols.SimConnect_AICreateSimulatedObject(hSimConnect, szContainerTitle, InitPos, RequestID);
+}
+
+SIMCONNECTAPI SimConnect_MapClientDataNameToID(HANDLE hSimConnect, const char *szClientDataName,
+ SIMCONNECT_CLIENT_DATA_ID ClientDataID)
+{
+ return gSymbols.SimConnect_MapClientDataNameToID(hSimConnect, szClientDataName, ClientDataID);
+}
+
+SIMCONNECTAPI SimConnect_CreateClientData(HANDLE hSimConnect, SIMCONNECT_CLIENT_DATA_ID ClientDataID, DWORD dwSize,
+ SIMCONNECT_CREATE_CLIENT_DATA_FLAG Flags)
+{
+ return gSymbols.SimConnect_CreateClientData(hSimConnect, ClientDataID, dwSize, Flags);
+}
+
+SIMCONNECTAPI SimConnect_AddToClientDataDefinition(HANDLE hSimConnect, SIMCONNECT_CLIENT_DATA_DEFINITION_ID DefineID,
+ DWORD dwOffset, DWORD dwSizeOrType, float fEpsilon, DWORD DatumID)
+{
+ return gSymbols.SimConnect_AddToClientDataDefinition(hSimConnect, DefineID, dwOffset, dwSizeOrType, fEpsilon,
+ DatumID);
+}
+
+#ifdef Q_OS_WIN64
+// SIMCONNECTAPI SimConnect_RequestGroundInfo(HANDLE hSimConnect, SIMCONNECT_DATA_REQUEST_ID RequestID, double minLat,
+// double minLon, double minAlt, double maxLat, double maxLon, double maxAlt,
+// DWORD dwGridWidth, DWORD dwGridHeight,
+// SIMCONNECT_GROUND_INFO_LATLON_FORMAT eLatLonFormat,
+// SIMCONNECT_GROUND_INFO_ALT_FORMAT eAltFormat,
+// SIMCONNECT_GROUND_INFO_SOURCE_FLAG eSourceFlags)
+//{
+// return gSymbols.SimConnect_RequestGroundInfo(hSimConnect, RequestID, minLat, minLon, minAlt, maxLat, maxLon,
+// maxAlt,
+// dwGridWidth, dwGridHeight, eLatLonFormat, eAltFormat, eSourceFlags);
+// }
+
+SIMCONNECTAPI SimConnect_ChangeView(HANDLE hSimConnect, const char *szName)
+{
+ return gSymbols.SimConnect_ChangeView(hSimConnect, szName);
+}
+
+SIMCONNECTAPI SimConnect_AIReleaseControlEx(HANDLE hSimConnect, SIMCONNECT_OBJECT_ID ObjectID,
+ SIMCONNECT_DATA_REQUEST_ID RequestID, BOOL destroyAI)
+{
+ return gSymbols.SimConnect_AIReleaseControlEx(hSimConnect, ObjectID, RequestID, destroyAI);
+}
+
+SIMCONNECTAPI SimConnect_ObserverAttachToEntityOn(HANDLE hSimConnect, const char *szName, DWORD dwObjectID,
+ SIMCONNECT_DATA_XYZ Offset)
+{
+ return gSymbols.SimConnect_ObserverAttachToEntityOn(hSimConnect, szName, dwObjectID, Offset);
+}
+
+// SIMCONNECTAPI SimConnect_CreateObserver(HANDLE hSimConnect, const char *szName, SIMCONNECT_DATA_OBSERVER
+// ObserverData)
+//{
+// return gSymbols.SimConnect_CreateObserver(hSimConnect, szName, ObserverData);
+// }
+
+SIMCONNECTAPI SimConnect_OpenView(HANDLE hSimConnect, const char *szName, const char *szTitle)
+{
+ return gSymbols.SimConnect_OpenView(hSimConnect, szName, szTitle);
+}
+
+SIMCONNECTAPI SimConnect_CloseView(HANDLE hSimConnect, const char *szName)
+{
+ return gSymbols.SimConnect_CloseView(hSimConnect, szName);
+}
+
+SIMCONNECTAPI SimConnect_SetObserverLookAt(HANDLE hSimConnect, const char *szName,
+ SIMCONNECT_DATA_LATLONALT TargetPosition)
+{
+ return gSymbols.SimConnect_SetObserverLookAt(hSimConnect, szName, TargetPosition);
+}
+
+#endif
diff --git a/src/plugins/simulator/msfs2024/simconnectsymbolsmsfs2024.h b/src/plugins/simulator/msfs2024/simconnectsymbolsmsfs2024.h
new file mode 100644
index 000000000..805701ff7
--- /dev/null
+++ b/src/plugins/simulator/msfs2024/simconnectsymbolsmsfs2024.h
@@ -0,0 +1,15 @@
+// SPDX-FileCopyrightText: Copyright (C) 2018 swift Project Community / Contributors
+// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
+
+//! \file
+
+#ifndef SWIFT_SIMPLUGIN_MSFS2024COMMON_SIMCONNECTSYMBOLS_H
+#define SWIFT_SIMPLUGIN_MSFS2024COMMON_SIMCONNECTSYMBOLS_H
+
+#include
+
+#include "plugins/simulator/msfs2024/msfs2024export.h"
+
+MSFS2024_EXPORT bool loadAndResolveMSFS2024SimConnect();
+
+#endif // SWIFT_SIMPLUGIN_MSFS2024COMMON_SIMCONNECTSYMBOLS_H
diff --git a/src/plugins/simulator/msfs2024/simconnectwindowsmsfs2024.h b/src/plugins/simulator/msfs2024/simconnectwindowsmsfs2024.h
new file mode 100644
index 000000000..b5c84a94c
--- /dev/null
+++ b/src/plugins/simulator/msfs2024/simconnectwindowsmsfs2024.h
@@ -0,0 +1,28 @@
+// SPDX-FileCopyrightText: Copyright (C) 2018 swift Project Community / Contributors
+// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
+
+// in P3Dv4 the simconnect.h does not include windows.h
+// here we include windows.h first
+
+#ifndef SWIFT_SIMPLUGIN_MSFS2024_SIMCONNECTWINDOWS_H
+#define SWIFT_SIMPLUGIN_MSFS2024_SIMCONNECTWINDOWS_H
+
+#ifndef NOMINMAX
+# define NOMINMAX
+#endif
+
+// clash with struct interface in objbase.h used to happen
+#pragma push_macro("interface")
+#undef interface
+
+// clang-format off
+#include
+//#include
+#include "../third_party/externals/common/include/simconnect/MSFS2024/SimConnect.h"
+// clang-format on
+
+#include
+
+#pragma pop_macro("interface")
+
+#endif // SWIFT_SIMPLUGIN_MSFS2024_SIMCONNECTWINDOWS_H
diff --git a/src/plugins/simulator/msfs2024/simulatormsfs2024.cpp b/src/plugins/simulator/msfs2024/simulatormsfs2024.cpp
index fa9497ed3..f0e0477d7 100644
--- a/src/plugins/simulator/msfs2024/simulatormsfs2024.cpp
+++ b/src/plugins/simulator/msfs2024/simulatormsfs2024.cpp
@@ -3,7 +3,7 @@
#include "simulatorMsFs2024.h"
-#include "../fsxcommon/simconnectsymbols.h"
+#include "../msfs2024/simconnectsymbolsmsfs2024.h"
using namespace swift::misc;
using namespace swift::misc::aviation;
@@ -13,23 +13,23 @@ using namespace swift::misc::network;
using namespace swift::misc::simulation;
using namespace swift::misc::simulation::fscommon;
using namespace swift::core;
-using namespace swift::simplugin::fsxcommon;
+using namespace swift::simplugin::msfs2024common;
namespace swift::simplugin::msfs2024
{
CSimulatorMsFs2024::CSimulatorMsFs2024(const CSimulatorPluginInfo &info, IOwnAircraftProvider *ownAircraftProvider,
IRemoteAircraftProvider *remoteAircraftProvider,
IClientProvider *clientProvider, QObject *parent)
- : CSimulatorFsxCommon(info, ownAircraftProvider, remoteAircraftProvider, clientProvider, parent)
+ : CSimulatorMsfs2024(info, ownAircraftProvider, remoteAircraftProvider, clientProvider, parent)
{
- this->setDefaultModel({ "Airbus A320 Neo Asobo", CAircraftModel::TypeModelMatchingDefaultModel,
+ this->setDefaultModel({ "A320neo V2", CAircraftModel::TypeModelMatchingDefaultModel,
"Airbus A320 default model", CAircraftIcaoCode("A320", "L2J") });
}
bool CSimulatorMsFs2024::connectTo()
{
if (!loadAndResolveMSFS2024SimConnect()) { return false; }
- return CSimulatorFsxCommon::connectTo();
+ return CSimulatorMsfs2024::connectTo();
}
void CSimulatorMsFs2024::setTrueAltitude(CAircraftSituation &aircraftSituation,
@@ -44,7 +44,7 @@ namespace swift::simplugin::msfs2024
void CSimulatorMsFs2024Listener::startImpl()
{
if (!loadAndResolveMSFS2024SimConnect()) { return; }
- CSimulatorFsxCommonListener::startImpl();
+ CSimulatorMsfs2024Listener::startImpl();
}
} // namespace swift::simplugin::msfs2024
diff --git a/src/plugins/simulator/msfs2024/simulatormsfs2024.h b/src/plugins/simulator/msfs2024/simulatormsfs2024.h
index 2ca045eee..579d04a41 100644
--- a/src/plugins/simulator/msfs2024/simulatormsfs2024.h
+++ b/src/plugins/simulator/msfs2024/simulatormsfs2024.h
@@ -6,12 +6,12 @@
#ifndef SWIFT_SIMPLUGIN_MSFS_SIMULATORMSFS2024_H
#define SWIFT_SIMPLUGIN_MSFS_SIMULATORMSFS2024_H
-#include "../fsxcommon/simulatorfsxcommon.h"
+#include "../msfs2024/simulatormsfs2024common.h"
namespace swift::simplugin::msfs2024
{
- //! FSX simulator implementation
- class CSimulatorMsFs2024 : public swift::simplugin::fsxcommon::CSimulatorFsxCommon
+ //! MSFS2024 simulator implementation
+ class CSimulatorMsFs2024 : public swift::simplugin::msfs2024common::CSimulatorMsfs2024
{
Q_OBJECT
@@ -27,19 +27,19 @@ namespace swift::simplugin::msfs2024
virtual bool connectTo() override;
//! @}
- virtual void
- setTrueAltitude(swift::misc::aviation::CAircraftSituation &aircraftSituation,
- const swift::simplugin::fsxcommon::DataDefinitionOwnAircraft &simulatorOwnAircraft) override;
+ virtual void setTrueAltitude(
+ swift::misc::aviation::CAircraftSituation &aircraftSituation,
+ const swift::simplugin::msfs2024common::DataDefinitionOwnAircraft &simulatorOwnAircraft) override;
};
//! Listener for MSFS2024
- class CSimulatorMsFs2024Listener : public fsxcommon::CSimulatorFsxCommonListener
+ class CSimulatorMsFs2024Listener : public swift::simplugin::msfs2024common::CSimulatorMsfs2024Listener
{
Q_OBJECT
public:
//! Constructor
- using CSimulatorFsxCommonListener::CSimulatorFsxCommonListener;
+ using CSimulatorMsfs2024Listener::CSimulatorMsfs2024Listener;
protected:
virtual void startImpl() override;
diff --git a/src/plugins/simulator/msfs2024/simulatormsfs2024common.cpp b/src/plugins/simulator/msfs2024/simulatormsfs2024common.cpp
new file mode 100644
index 000000000..33bcd7895
--- /dev/null
+++ b/src/plugins/simulator/msfs2024/simulatormsfs2024common.cpp
@@ -0,0 +1,3331 @@
+// SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors
+// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
+
+#include "simulatormsfs2024common.h"
+
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include "../fscommon/simulatorfscommonfunctions.h"
+#include "simconnectsymbolsmsfs2024.h"
+
+#include "config/buildconfig.h"
+#include "core/application.h"
+#include "core/modelsetbuilder.h"
+#include "core/webdataservices.h"
+#include "core/webdataservicesms.h"
+#include "gui/components/dbmappingcomponent.h"
+#include "gui/guiapplication.h"
+#include "gui/models/distributorlistmodel.h"
+#include "misc/aviation/airportlist.h"
+#include "misc/country.h"
+#include "misc/geo/elevationplane.h"
+#include "misc/logmessage.h"
+#include "misc/math/mathutils.h"
+#include "misc/network/textmessage.h"
+#include "misc/simulation/aircraftmodel.h"
+#include "misc/simulation/data/modelcaches.h"
+#include "misc/simulation/distributorlist.h"
+#include "misc/simulation/fscommon/aircraftcfgparser.h"
+#include "misc/simulation/fscommon/bcdconversions.h"
+#include "misc/simulation/fscommon/fscommonutil.h"
+#include "misc/simulation/fsx/simconnectutilities.h"
+#include "misc/simulation/interpolation/interpolatormulti.h"
+#include "misc/simulation/settings/simulatorsettings.h"
+#include "misc/simulation/simulatorplugininfo.h"
+#include "misc/statusmessagelist.h"
+#include "misc/threadutils.h"
+#include "misc/verify.h"
+#include "misc/worker.h"
+
+using namespace swift::config;
+using namespace swift::misc;
+using namespace swift::misc::aviation;
+using namespace swift::misc::physical_quantities;
+using namespace swift::misc::geo;
+using namespace swift::misc::network;
+using namespace swift::misc::math;
+using namespace swift::misc::simulation;
+using namespace swift::misc::simulation::data;
+using namespace swift::misc::simulation::fscommon;
+using namespace swift::misc::simulation::fsx;
+// using namespace swift::misc::simulation::msfs2024;
+using namespace swift::misc::simulation::settings;
+using namespace swift::core;
+using namespace swift::core::db;
+using namespace swift::simplugin::fscommon;
+using namespace swift::gui;
+using namespace swift::gui::models;
+using namespace swift::gui::components;
+
+namespace swift::simplugin::msfs2024common
+{
+
+ CSimulatorMsfs2024::CSimulatorMsfs2024(const CSimulatorPluginInfo &info, IOwnAircraftProvider *ownAircraftProvider,
+ IRemoteAircraftProvider *remoteAircraftProvider,
+ IClientProvider *clientProvider, QObject *parent)
+ : CSimulatorFsCommon(info, ownAircraftProvider, remoteAircraftProvider, clientProvider, parent)
+ {
+ Q_ASSERT_X(ownAircraftProvider, Q_FUNC_INFO, "Missing provider");
+ Q_ASSERT_X(remoteAircraftProvider, Q_FUNC_INFO, "Missing provider");
+ Q_ASSERT_X(sApp, Q_FUNC_INFO, "Missing global object");
+
+ m_simObjectTimer.setInterval(AddPendingAircraftIntervalMs);
+ // default model will be set in derived class
+
+ CSimulatorMsfs2024::registerHelp();
+ connect(&m_simObjectTimer, &QTimer::timeout, this, &CSimulatorMsfs2024::timerBasedObjectAddOrRemove);
+
+ // m_distributorPreferences = std::make_shared();
+ }
+
+ CSimulatorMsfs2024::~CSimulatorMsfs2024() { this->disconnectFrom(); }
+
+ bool CSimulatorMsfs2024::isConnected() const { return m_simConnected && m_hSimConnect; }
+
+ bool CSimulatorMsfs2024::isSimulating() const { return m_simSimulating && this->isConnected(); }
+
+ bool CSimulatorMsfs2024::connectTo()
+ {
+ if (this->isConnected()) { return true; }
+ this->reset();
+
+ const HRESULT hr = SimConnect_Open(&m_hSimConnect, sApp->swiftVersionChar(), nullptr, 0, nullptr, 0);
+ if (isFailure(hr))
+ {
+ // reset state as expected for unconnected
+ this->reset();
+ return false;
+ }
+
+ // set structures and move on
+ this->triggerAutoTraceSendId(); // we trace the init phase, so in case something goes wrong there
+ this->initEvents();
+ this->initEventsP3D();
+ this->initDataDefinitionsWhenConnected();
+
+ m_timerId = this->startTimer(DispatchIntervalMs);
+ // do not start m_addPendingAircraftTimer here, it will be started when object was added
+ return true;
+ }
+
+ bool CSimulatorMsfs2024::disconnectFrom()
+ {
+ if (!m_simConnected) { return true; }
+ m_simSimulating = false; // thread as stopped, just setting the flag here avoids overhead of on onSimStopped
+ m_traceAutoUntilTs = -1;
+ m_traceSendId = false;
+ this->reset(); // mark as disconnected and reset all values
+
+ if (m_hSimConnect)
+ {
+ SimConnect_Close(m_hSimConnect);
+ m_hSimConnect = nullptr;
+ m_simConnected = false;
+ }
+
+ // emit status
+ return CSimulatorFsCommon::disconnectFrom();
+ }
+
+ bool CSimulatorMsfs2024::physicallyAddRemoteAircraft(const CSimulatedAircraft &newRemoteAircraft)
+ {
+ this->logAddingAircraftModel(newRemoteAircraft);
+ return this->physicallyAddRemoteAircraftImpl(newRemoteAircraft, ExternalCall);
+ }
+
+ bool CSimulatorMsfs2024::updateCOMFromSwiftToSimulator(const CFrequency &newFreq, const CFrequency &lastSimFreq,
+ CFrequency &last25kHzSimFreq, EventIds id)
+ {
+ if (newFreq == lastSimFreq) { return false; }
+
+ if (CComSystem::isExclusiveWithin8_33kHzChannel(newFreq) && last25kHzSimFreq.isNull())
+ {
+ // Switch from 25 to 8.33
+ // Store last 25 kHz frequency and do not send to simulator
+ last25kHzSimFreq = lastSimFreq;
+ return false;
+ }
+
+ if (CComSystem::isWithin25kHzChannel(newFreq))
+ {
+ // Send to simulator
+ last25kHzSimFreq.setNull();
+ SimConnect_TransmitClientEvent(m_hSimConnect, 0, id, CBcdConversions::comFrequencyToBcdHz(newFreq),
+ SIMCONNECT_GROUP_PRIORITY_HIGHEST,
+ SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY);
+ return true;
+ }
+
+ // Already 8.33 -> nothing to do
+ return false;
+ }
+
+ bool CSimulatorMsfs2024::updateOwnSimulatorCockpit(const CSimulatedAircraft &ownAircraft,
+ const CIdentifier &originator)
+ {
+ if (originator == this->identifier()) { return false; }
+ if (!this->isSimulating()) { return false; }
+
+ // actually those data should be the same as ownAircraft
+ const CComSystem newCom1 = ownAircraft.getCom1System();
+ const CComSystem newCom2 = ownAircraft.getCom2System();
+ const CTransponder newTransponder = ownAircraft.getTransponder();
+
+ bool changed = false;
+
+ changed |= updateCOMFromSwiftToSimulator(newCom1.getFrequencyActive(), m_simCom1.getFrequencyActive(),
+ m_lastCom1Active, EventSetCom1Active);
+ changed |= updateCOMFromSwiftToSimulator(newCom1.getFrequencyStandby(), m_simCom1.getFrequencyStandby(),
+ m_lastCom1Standby, EventSetCom1Standby);
+ changed |= updateCOMFromSwiftToSimulator(newCom2.getFrequencyActive(), m_simCom2.getFrequencyActive(),
+ m_lastCom2Active, EventSetCom2Active);
+ changed |= updateCOMFromSwiftToSimulator(newCom2.getFrequencyStandby(), m_simCom2.getFrequencyStandby(),
+ m_lastCom2Standby, EventSetCom2Standby);
+
+ if (newTransponder.getTransponderMode() != m_simTransponder.getTransponderMode())
+ {
+ if (m_useSbOffsets)
+ {
+ byte ident = newTransponder.isIdentifying() ? 1U : 0U; // 1 is ident
+ byte standby = newTransponder.isInStandby() ? 1U : 0U; // 1 is standby
+ HRESULT hr = s_ok();
+
+ hr += SimConnect_SetClientData(m_hSimConnect, ClientAreaSquawkBox,
+ CSimConnectDefinitions::DataClientAreaSbIdent,
+ SIMCONNECT_CLIENT_DATA_SET_FLAG_DEFAULT, 0, 1, &ident);
+ hr += SimConnect_SetClientData(m_hSimConnect, ClientAreaSquawkBox,
+ CSimConnectDefinitions::DataClientAreaSbStandby,
+ SIMCONNECT_CLIENT_DATA_SET_FLAG_DEFAULT, 0, 1, &standby);
+ if (isFailure(hr))
+ {
+ this->triggerAutoTraceSendId();
+ CLogMessage(this).warning(u"Setting transponder mode failed (SB offsets)");
+ }
+ else
+ {
+ if (m_logSbOffsets)
+ {
+ const QString lm =
+ "SB sent: ident " % QString::number(ident) % u" standby " % QString::number(standby);
+ CLogMessage(this).info(lm);
+ }
+ }
+ changed = true;
+ }
+ else if (this->getSimulatorPluginInfo().getSimulatorInfo().isMSFS2024())
+ {
+ DataDefinitionMSFSTransponderMode t;
+ t.transponderMode = (newTransponder.isInStandby() ? 1 : 4);
+ t.ident = newTransponder.isIdentifying();
+
+ HRESULT hr = s_ok();
+
+ hr += SimConnect_SetDataOnSimObject(m_hSimConnect, CSimConnectDefinitions::DataTransponderModeMSFS,
+ SIMCONNECT_OBJECT_ID_USER, SIMCONNECT_DATA_SET_FLAG_DEFAULT, 0,
+ sizeof(DataDefinitionMSFSTransponderMode), &t);
+
+ if (isFailure(hr)) { CLogMessage(this).warning(u"Setting transponder mode failed (MSFS2024)"); }
+
+ changed = true;
+ }
+ }
+
+ // avoid changes of cockpit back to old values due to an outdated read back value
+ if (changed) { m_skipCockpitUpdateCycles = SkipUpdateCyclesForCockpit; }
+
+ // bye
+ return changed;
+ }
+
+ bool CSimulatorMsfs2024::updateOwnSimulatorSelcal(const CSelcal &selcal, const CIdentifier &originator)
+ {
+ if (originator == this->identifier()) { return false; }
+ if (!this->isSimulating()) { return false; }
+
+ //! KB 2018-11 that would need to go to updateOwnAircraftFromSimulator if the simulator ever supports SELCAL
+ //! KB 2018-11 als we would need to send the value to FS9/FSX (currently we only deal with it on FS9/FSX level)
+ m_selcal = selcal;
+ return false;
+ }
+
+ void CSimulatorMsfs2024::displayStatusMessage(const CStatusMessage &message) const
+ {
+ QByteArray m = message.getMessage().toLatin1().constData();
+ m.append('\0');
+
+ SIMCONNECT_TEXT_TYPE type = SIMCONNECT_TEXT_TYPE_PRINT_BLACK;
+ switch (message.getSeverity())
+ {
+ case CStatusMessage::SeverityDebug: return;
+ case CStatusMessage::SeverityInfo: type = SIMCONNECT_TEXT_TYPE_PRINT_GREEN; break;
+ case CStatusMessage::SeverityWarning: type = SIMCONNECT_TEXT_TYPE_PRINT_YELLOW; break;
+ case CStatusMessage::SeverityError: type = SIMCONNECT_TEXT_TYPE_PRINT_RED; break;
+ }
+ const HRESULT hr =
+ SimConnect_Text(m_hSimConnect, type, 7.5, EventTextMessage, static_cast(m.size()), m.data());
+ Q_UNUSED(hr)
+ }
+
+ void CSimulatorMsfs2024::displayTextMessage(const CTextMessage &message) const
+ {
+ QByteArray m = message.asString(true, true).toLatin1().constData();
+ m.append('\0');
+
+ SIMCONNECT_TEXT_TYPE type = SIMCONNECT_TEXT_TYPE_PRINT_BLACK;
+ if (message.isSupervisorMessage()) { type = SIMCONNECT_TEXT_TYPE_PRINT_RED; }
+ else if (message.isPrivateMessage()) { type = SIMCONNECT_TEXT_TYPE_PRINT_YELLOW; }
+ else if (message.isRadioMessage()) { type = SIMCONNECT_TEXT_TYPE_PRINT_GREEN; }
+
+ const HRESULT hr =
+ SimConnect_Text(m_hSimConnect, type, 7.5, EventTextMessage, static_cast(m.size()), m.data());
+ Q_UNUSED(hr)
+ }
+
+ bool CSimulatorMsfs2024::isPhysicallyRenderedAircraft(const CCallsign &callsign) const
+ {
+ return m_simConnectObjects.contains(callsign);
+ }
+
+ CCallsignSet CSimulatorMsfs2024::physicallyRenderedAircraft() const
+ {
+ CCallsignSet callsigns(m_simConnectObjects.keys());
+ callsigns.push_back(
+ m_addAgainAircraftWhenRemoved.getCallsigns()); // not really rendered right now, but very soon
+ callsigns.push_back(
+ m_addPendingAircraft.keys()); // not really rendered, but for the logic it should look like it is
+ return CCallsignSet(m_simConnectObjects.keys());
+ }
+
+ CStatusMessageList CSimulatorMsfs2024::debugVerifyStateAfterAllAircraftRemoved() const
+ {
+ CStatusMessageList msgs;
+ if (!CBuildConfig::isLocalDeveloperDebugBuild()) { return msgs; }
+ msgs = CSimulatorFsCommon::debugVerifyStateAfterAllAircraftRemoved();
+ if (!m_simConnectObjects.isEmpty())
+ {
+ msgs.push_back(CStatusMessage(this).error(u"m_simConnectObjects not empty: '%1'")
+ << m_simConnectObjects.getAllCallsignStringsAsString(true));
+ }
+ if (!m_simConnectObjectsPositionAndPartsTraces.isEmpty())
+ {
+ msgs.push_back(CStatusMessage(this).error(u"m_simConnectObjectsPositionAndPartsTraces not empty: '%1'")
+ << m_simConnectObjectsPositionAndPartsTraces.getAllCallsignStringsAsString(true));
+ }
+ if (!m_addAgainAircraftWhenRemoved.isEmpty())
+ {
+ msgs.push_back(CStatusMessage(this).error(u"m_addAgainAircraftWhenRemoved not empty: '%1'")
+ << m_addAgainAircraftWhenRemoved.getCallsignStrings(true).join(", "));
+ }
+ return msgs;
+ }
+
+ QString CSimulatorMsfs2024::getStatisticsSimulatorSpecific() const
+ {
+ static const QString specificInfo(
+ "dispatch #: %1 %2 times (cur/max): %3ms (%4ms) %5ms (%6ms) %7 %8 simData#: %9");
+ return specificInfo.arg(m_dispatchProcCount)
+ .arg(m_dispatchProcEmptyCount)
+ .arg(m_dispatchTimeMs)
+ .arg(m_dispatchProcTimeMs)
+ .arg(m_dispatchMaxTimeMs)
+ .arg(m_dispatchProcMaxTimeMs)
+ .arg(CSimConnectUtilities::simConnectReceiveIdToString(static_cast(m_dispatchReceiveIdMaxTime)),
+ requestIdToString(m_dispatchRequestIdMaxTime))
+ .arg(m_requestSimObjectDataCount);
+ }
+
+ bool CSimulatorMsfs2024::requestElevation(const ICoordinateGeodetic &reference, const CCallsign &aircraftCallsign)
+ {
+ // this is the 32bit FSX version, the P3D x64 is overridden!
+
+ if (this->isShuttingDownOrDisconnected()) { return false; }
+ if (!this->isUsingFsxTerrainProbe()) { return false; }
+ if (reference.isNull()) { return false; }
+ const CSimConnectObject simObject = m_simConnectObjects.getOldestNotPendingProbe(); // probes round robin
+ if (!simObject.isConfirmedAdded()) { return false; }
+ m_simConnectObjects[simObject.getCallsign()].resetTimestampToNow(); // mark probe as just used
+
+ CCoordinateGeodetic pos(reference);
+ pos.setGeodeticHeight(terrainProbeAltitude());
+
+ SIMCONNECT_DATA_INITPOSITION position = this->coordinateToFsxPosition(pos);
+ const HRESULT hr = this->logAndTraceSendId(
+ SimConnect_SetDataOnSimObject(m_hSimConnect, CSimConnectDefinitions::DataRemoteAircraftSetPosition,
+ simObject.getObjectId(), 0, 0, sizeof(SIMCONNECT_DATA_INITPOSITION),
+ &position),
+ simObject, "Cannot request AI elevation", Q_FUNC_INFO, "SimConnect_SetDataOnSimObject");
+
+ if (isFailure(hr)) { return false; }
+
+ const bool ok = this->requestTerrainProbeData(simObject, aircraftCallsign);
+ if (ok) { emit this->requestedElevation(aircraftCallsign); }
+ return ok;
+ }
+
+ void CSimulatorMsfs2024::CacheSimObjectAndLiveries(const SIMCONNECT_RECV_ENUMERATE_SIMOBJECT_AND_LIVERY_LIST *msg)
+ {
+ for (unsigned i = 0; i < msg->dwArraySize; ++i)
+ {
+ const SIMCONNECT_ENUMERATE_SIMOBJECT_LIVERY &element = msg->rgData[i];
+
+ // combine aircraft title and livery name for database search
+ // const QString combined = QString(element.AircraftTitle) + " " + QString(element.LiveryName);
+
+ vSimObjectsAndLiveries.push_back(
+ { QString("%1 %2").arg(element.AircraftTitle, element.LiveryName).trimmed(),
+ QString::fromUtf8(element.AircraftTitle), QString::fromUtf8(element.LiveryName) });
+ }
+
+ if (!sSimmobjectLoadedState.bLoadStarted)
+ {
+ sSimmobjectLoadedState.bLoadStarted = true;
+ CLogMessage(this).info(u"Start loading SimObjects and liverys from simulator");
+
+ // TODO TZ a message should be displayed here because the gui freezes during loading large amounts of data
+ const CStatusMessage m = CStatusMessage(this, CStatusMessage::SeverityInfo,
+ u"Start loading SimObjects and liverys from simulator");
+ }
+
+ // Check the result set to detect the end of the transmission
+ switch (msg->dwRequestID)
+ {
+ case CSimConnectDefinitions::REQUEST_AIRPLANE:
+ if (msg->dwArraySize < 79) sSimmobjectLoadedState.bAirplaneLoaded = true;
+ break;
+ case CSimConnectDefinitions::REQUEST_HELICOPTER:
+ if (msg->dwArraySize < 79) sSimmobjectLoadedState.bHelicopterLoaded = true;
+ break;
+ case CSimConnectDefinitions::REQUEST_HOT_AIR:
+ if (msg->dwArraySize < 79) sSimmobjectLoadedState.bHotAirLoaded = true;
+ break;
+
+ default: break;
+ }
+
+ // sSimmobjectLoadedState.bHotAirLoaded
+ if (sSimmobjectLoadedState.bAirplaneLoaded && sSimmobjectLoadedState.bHelicopterLoaded)
+ {
+ sSimmobjectLoadedState.bLoadStarted = false;
+ size_t countmodels = vSimObjectsAndLiveries.size();
+ CLogMessage(this).info(u"%1 SimObjects and Liveries loaded from SimConnect") << countmodels;
+
+ // now we try to create a new temporary model list
+ setSimObjectAndLiveries();
+
+ CLogMessage(this).info(u"finished new model set");
+ }
+ }
+
+ void CSimulatorMsfs2024::setSimObjectAndLiveries()
+ {
+ // TODO TZ a message should be displayed here because the gui freezes during loading
+ // better: move to the background (e.g., use CWorker::fromTask(...)), avoid GUI freeze.
+
+ this->createNewModelList();
+
+ CLogMessage(this).info(u"%1 SimObjects and Liveries in vSimObjectsAndLiveries")
+ << vSimObjectsAndLiveries.size();
+ // Cannot create children for a parent that is in a different thread.
+ // owner = this (QObject in main thread)
+ // auto *worker = CWorker::fromTask(this, "createNewModelList", [=]() {
+ // this->createNewModelList();
+ // return QVariant(); // void-Result
+ //});
+
+ // TODO TZ where to place this message?
+ // worker->then(this, [=] { CLogMessage(this).info(u"SimObjects and Liveries in vSimObjectsAndLiveries ready");
+ // });
+ }
+
+ void CSimulatorMsfs2024::createNewModelList()
+ {
+
+ const CSpecializedSimulatorSettings settings = this->getSimulatorSettings();
+ CSimulatorSettings m_generic = settings.getGenericSettings();
+ QStringList excludePatterns = m_generic.getModelExcludeDirectoryPatterns();
+
+ bool gui_application = true;
+ QString guiName = sGui->getApplicationName();
+ if (guiName.contains("mapping")) gui_application = false;
+
+ CAircraftModelList newModels;
+
+ for (int i = 0; i < static_cast(vSimObjectsAndLiveries.size()); ++i)
+ {
+ const sSimObjectLivery &modelLivery = vSimObjectsAndLiveries[i];
+
+ CAircraftModel model;
+ CAircraftModel modelFromDb =
+ sGui->getWebDataServices()->getModelForModelString(modelLivery.szSimObjectCombinedTitle.trimmed());
+
+ // If the model is in the database, there is a DbKey
+ int modelkey = modelFromDb.getDbKey();
+ if (modelkey) model = modelFromDb; // copy all data from db
+
+ // we need the combined string of the model for further processing
+ model.setModelString(modelLivery.szSimObjectCombinedTitle);
+ model.setModelLivery(modelLivery.szLiveryName);
+ model.setModelType(CAircraftModel::TypeOwnSimulatorModel);
+ model.setSimulator(this->getSimulatorInfo());
+
+ bool excluded = false;
+ for (const QString &rawPattern : excludePatterns)
+ {
+ const QString pattern = rawPattern.trimmed();
+ if (pattern.isEmpty()) continue;
+ if (model.getModelString().contains(pattern, Qt::CaseInsensitive))
+ {
+ excluded = true;
+ break;
+ }
+ }
+ if (excluded) continue; // skip adding this model
+
+ newModels.replaceOrAddModelWithString(model, Qt::CaseInsensitive);
+ }
+
+ CAircraftModelList newModelList;
+ CAircraftModelList currentSet;
+ CAircraftModelList NewSet;
+
+ // store the model in the new list
+ newModelList.replaceOrAddModelsWithString(newModels, Qt::CaseInsensitive);
+
+ CLogMessage(this).info(u"%1 SimObjects and Liveries in newModelList") << newModelList.size();
+
+ if (!newModelList.isEmpty())
+ {
+
+ m_simulatorInfo = this->getSimulatorInfo();
+
+ bool givenDistributorsOnly = false;
+ bool dbDataOnly = false;
+ bool dbIcaoOnly = false;
+ bool incremnental = true;
+ bool sortByDistributor = true;
+ bool consolidateWithDb = false;
+ bool ShowAllInstalledModells = true;
+
+ if (gui_application)
+ {
+ givenDistributorsOnly = m_generic.getPropertyDistributorFiltered();
+ dbDataOnly = m_generic.getPropertyWithDbEntry();
+ dbIcaoOnly = false;
+ incremnental = false;
+ sortByDistributor = true;
+ consolidateWithDb = true;
+ ShowAllInstalledModells = true; // msfs20424 always show all installed models
+ }
+
+ // CDistributorList distributorList;
+ // for (const QString &name : distributorNames) { distributorList.push_back(CDistributor(name)); }
+ CDistributorList distributorList = sGui->getWebDataServices()->getDistributors();
+
+ const CModelSetBuilder builder(this);
+ CModelSetBuilder::Builder options =
+ givenDistributorsOnly ? CModelSetBuilder::GivenDistributorsOnly : CModelSetBuilder::NoOptions;
+ if (dbDataOnly) { options |= CModelSetBuilder::OnlyDbData; }
+ if (dbIcaoOnly) { options |= CModelSetBuilder::OnlyDbIcaoCodes; }
+ if (incremnental) { options |= CModelSetBuilder::Incremental; }
+ if (sortByDistributor) { options |= CModelSetBuilder::SortByDistributors; }
+ if (consolidateWithDb) { options |= CModelSetBuilder::ConsolidateWithDb; }
+ if (ShowAllInstalledModells) { options |= CModelSetBuilder::ShowAllInstalledModells; }
+ const CSimulatorInfo &simulator = this->getSimulatorInfo();
+
+ CCentralMultiSimulatorModelSetCachesProvider::modelCachesInstance().synchronizeCache(simulator);
+ currentSet = CCentralMultiSimulatorModelSetCachesProvider::modelCachesInstance().getCachedModels(simulator);
+
+ NewSet = builder.buildModelSet(simulator, newModelList, currentSet, options, distributorList);
+
+ CAircraftMatcher matcher;
+ swift::misc::simulation::CAircraftMatcherSetup mSetup = matcher.getSetup();
+
+ NewSet.setSimulatorInfo(simulator);
+ matcher.setModelSet(NewSet, m_simulatorInfo, true);
+
+ const QDateTime latestDbModelsTs =
+ NewSet.isEmpty() ? sApp->getWebDataServices()->getCacheTimestamp(CEntityFlags::ModelEntity) :
+ NewSet.latestTimestamp();
+ if (!latestDbModelsTs.isValid()) { return; }
+
+ // for swiftgui it is enough to set the cache here
+ if (gui_application)
+ CCentralMultiSimulatorModelSetCachesProvider::modelCachesInstance().setModelsForSimulator(NewSet,
+ simulator);
+
+ CCentralMultiSimulatorModelCachesProvider::modelCachesInstance().setCachedModels(NewSet, simulator);
+
+ const CStatusMessage m = CStatusMessage(this, CStatusMessage::SeverityInfo,
+ u"Loading SimObjects and Liveries from the Simulator completed");
+
+ CLogMessage(this).info(u"%1 SimObjects and Liveries in DbModelList") << NewSet.size();
+
+ // TODO TZ only for debugging
+ // int cstoremodels = writeSimObjectsAndLiveriesToFile(NewSet);
+ }
+ }
+
+ int CSimulatorMsfs2024::writeSimObjectsAndLiveriesToFile(CAircraftModelList Modelset)
+ {
+
+ int counter = 0;
+ const std::string dateiname = "aircraftlist.txt";
+ std::ofstream datei(dateiname, std::ios::out);
+ if (!datei) return 0;
+ for (int i = 0; i < static_cast(Modelset.size()); ++i)
+ {
+ datei << Modelset[i].getShortModelString().toStdString()
+ << "::" << Modelset[i].getModelLivery().toStdString() << "::" << Modelset[i].getDbKey() << std::endl;
+ ++counter;
+ }
+ datei.close();
+ return counter;
+ }
+
+ bool CSimulatorMsfs2024::isTracingSendId() const
+ {
+ if (m_traceSendId) { return true; } // explicit
+ if (m_traceAutoUntilTs < 0) { return false; } // no auto
+ const qint64 ts = QDateTime::currentMSecsSinceEpoch();
+ const bool trace = ts <= m_traceAutoUntilTs;
+ return trace;
+ }
+
+ void CSimulatorMsfs2024::setTractingSendId(bool trace)
+ {
+ m_traceSendId = trace;
+ m_traceAutoUntilTs = -1;
+ }
+
+ void CSimulatorMsfs2024::setAddingAsSimulatedObjectEnabled(bool enabled)
+ {
+ m_useAddSimulatedObj = enabled;
+ const CSimulatorInfo sim = this->getSimulatorInfo();
+ CFsxP3DSettings settings = m_detailsSettings.getSettings(sim);
+ settings.setAddingAsSimulatedObjectEnabled(enabled);
+ m_detailsSettings.setSettings(settings, sim);
+ }
+
+ void CSimulatorMsfs2024::setUsingSbOffsetValues(bool enabled)
+ {
+ m_useSbOffsets = enabled;
+ const CSimulatorInfo sim = this->getSimulatorInfo();
+ CFsxP3DSettings settings = m_detailsSettings.getSettings(sim);
+ settings.setSbOffsetsEnabled(enabled);
+ m_detailsSettings.setSettings(settings, sim);
+ }
+
+ void CSimulatorMsfs2024::resetAircraftStatistics()
+ {
+ m_dispatchProcCount = 0;
+ m_dispatchProcEmptyCount = 0;
+ m_dispatchMaxTimeMs = -1;
+ m_dispatchProcMaxTimeMs = -1;
+ m_dispatchTimeMs = -1;
+ m_dispatchProcTimeMs = -1;
+ m_requestSimObjectDataCount = 0;
+ m_dispatchReceiveIdLast = SIMCONNECT_RECV_ID_NULL;
+ m_dispatchReceiveIdMaxTime = SIMCONNECT_RECV_ID_NULL;
+ m_dispatchRequestIdLast = CSimConnectDefinitions::RequestEndMarker;
+ m_dispatchRequestIdMaxTime = CSimConnectDefinitions::RequestEndMarker;
+ CSimulatorPluginCommon::resetAircraftStatistics();
+ }
+
+ void CSimulatorMsfs2024::setFlightNetworkConnected(bool connected)
+ {
+ // toggled?
+ if (connected == !this->isFlightNetworkConnected())
+ {
+ // toggling, we trace for a while to better monitor those "critical" phases
+ this->triggerAutoTraceSendId();
+ }
+
+ // update SB area network connected
+ byte sbNetworkConnected = connected ? 1u : 0u;
+ const HRESULT hr = SimConnect_SetClientData(m_hSimConnect, ClientAreaSquawkBox,
+ CSimConnectDefinitions::DataClientAreaSbConnected,
+ SIMCONNECT_CLIENT_DATA_SET_FLAG_DEFAULT, 0, 1, &sbNetworkConnected);
+ if (isFailure(hr)) { CLogMessage(this).warning(u"Setting network connected failed (SB offsets)"); }
+
+ ISimulator::setFlightNetworkConnected(connected);
+ }
+
+ CStatusMessageList CSimulatorMsfs2024::getInterpolationMessages(const CCallsign &callsign) const
+ {
+ if (!m_simConnectObjects.contains(callsign)) { return CStatusMessageList(); }
+ const CInterpolationAndRenderingSetupPerCallsign setup =
+ this->getInterpolationSetupConsolidated(callsign, false);
+ return (m_simConnectObjects[callsign]).getInterpolationMessages(setup.getInterpolatorMode());
+ }
+
+ bool CSimulatorMsfs2024::testSendSituationAndParts(const CCallsign &callsign, const CAircraftSituation &situation,
+ const CAircraftParts &parts)
+ {
+ if (!m_simConnectObjects.contains(callsign)) { return false; }
+ CSimConnectObject simObject = m_simConnectObjects.value(callsign);
+ int u = 0;
+ if (!parts.isNull())
+ {
+ this->sendRemoteAircraftPartsToSimulator(simObject, parts);
+ u++;
+ }
+ if (!situation.isNull())
+ {
+ SIMCONNECT_DATA_INITPOSITION position = this->aircraftSituationToFsxPosition(situation, true);
+ const bool traceSendId = this->isTracingSendId();
+ const HRESULT hr = this->logAndTraceSendId(
+ SimConnect_SetDataOnSimObject(m_hSimConnect, CSimConnectDefinitions::DataRemoteAircraftSetPosition,
+ static_cast(simObject.getObjectId()), 0, 0,
+ sizeof(SIMCONNECT_DATA_INITPOSITION), &position),
+ traceSendId, simObject, "Failed to set position", Q_FUNC_INFO, "SimConnect_SetDataOnSimObject");
+ if (hr == S_OK) { u++; }
+ }
+ return u > 0;
+ }
+
+ CSimConnectDefinitions::SimObjectRequest CSimulatorMsfs2024::requestToSimObjectRequest(DWORD requestId)
+ {
+ DWORD v = static_cast(CSimConnectDefinitions::SimObjectEndMarker);
+ if (isRequestForSimObjAircraft(requestId)) { v = (requestId - RequestSimObjAircraftStart) / MaxSimObjAircraft; }
+ else if (isRequestForSimObjTerrainProbe(requestId))
+ {
+ v = (requestId - RequestSimObjTerrainProbeStart) / MaxSimObjProbes;
+ }
+ Q_ASSERT_X(v <= CSimConnectDefinitions::SimObjectEndMarker, Q_FUNC_INFO, "Invalid value");
+ return static_cast(v);
+ }
+
+ bool CSimulatorMsfs2024::stillDisplayReceiveExceptions()
+ {
+ m_receiveExceptionCount++;
+ return m_receiveExceptionCount < IgnoreReceiveExceptions;
+ }
+
+ CSimConnectObject CSimulatorMsfs2024::getSimObjectForObjectId(DWORD objectId) const
+ {
+ return this->getSimConnectObjects().getSimObjectForObjectId(objectId);
+ }
+
+ void CSimulatorMsfs2024::setSimConnected()
+ {
+ m_simConnected = true;
+ this->initSimulatorInternals();
+ this->emitSimulatorCombinedStatus();
+
+ // Internals depends on simulator data which take a while to be read
+ // this is a trick and I re-init again after a while (which is not really expensive)
+ const QPointer myself(this);
+ QTimer::singleShot(2500, this, [myself] {
+ if (!myself) { return; }
+ myself->initSimulatorInternals();
+ });
+ }
+
+ void CSimulatorMsfs2024::onSimRunning()
+ {
+ const QPointer myself(this);
+ QTimer::singleShot(DeferSimulatingFlagMs, this, [=] {
+ if (!myself) { return; }
+ m_simulatingChangedTs = QDateTime::currentMSecsSinceEpoch();
+ this->onSimRunningDeferred(m_simulatingChangedTs);
+ });
+ }
+
+ void CSimulatorMsfs2024::onSimRunningDeferred(qint64 referenceTs)
+ {
+ if (m_simSimulating) { return; } // already simulatig
+ if (referenceTs != m_simulatingChangedTs) { return; } // changed, so no longer valid
+ m_simSimulating = true; // only place where this should be set to true
+ m_simConnected = true;
+
+ const CFsxP3DSettings settings = m_detailsSettings.getSettings(this->getSimulatorInfo());
+ m_useAddSimulatedObj = settings.isAddingAsSimulatedObjectEnabled();
+ m_useSbOffsets = settings.isSbOffsetsEnabled();
+ if (this->getSimulatorPluginInfo().getSimulatorInfo().isMSFS2024())
+ {
+ m_useSbOffsets = false; // Always disable SbOffsets for MSFS. Using new transponder mode property directly
+ }
+
+ HRESULT hr = s_ok();
+ hr += this->logAndTraceSendId(
+ SimConnect_RequestDataOnSimObject(m_hSimConnect, CSimConnectDefinitions::RequestOwnAircraft,
+ CSimConnectDefinitions::DataOwnAircraft, SIMCONNECT_OBJECT_ID_USER,
+ SIMCONNECT_PERIOD_VISUAL_FRAME),
+ "Cannot request own aircraft data", Q_FUNC_INFO, "SimConnect_RequestDataOnSimObject");
+
+ hr += this->logAndTraceSendId(
+ SimConnect_RequestDataOnSimObjectType(m_hSimConnect, CSimConnectDefinitions::RequestOwnAircraftTitle,
+ CSimConnectDefinitions::DataOwnAircraftTitle, 0,
+ SIMCONNECT_SIMOBJECT_TYPE_USER),
+ "Cannot request title and livery", Q_FUNC_INFO, "SimConnect_RequestDataOnSimObjectType");
+
+ hr += this->logAndTraceSendId(SimConnect_RequestDataOnSimObject(
+ m_hSimConnect, CSimConnectDefinitions::RequestMSFSTransponder,
+ CSimConnectDefinitions::DataTransponderModeMSFS, SIMCONNECT_OBJECT_ID_USER,
+ SIMCONNECT_PERIOD_VISUAL_FRAME, SIMCONNECT_DATA_REQUEST_FLAG_CHANGED),
+ "Cannot request MSFS transponder data", Q_FUNC_INFO,
+ "SimConnect_RequestDataOnSimObject");
+
+ if (isFailure(hr)) { return; }
+ this->emitSimulatorCombinedStatus(); // force sending status
+ }
+
+ void CSimulatorMsfs2024::onSimStopped()
+ {
+ // stopping events in FSX: Load menu, weather and season
+ CLogMessage(this).info(u"Simulator stopped: %1") << this->getSimulatorDetails();
+ const SimulatorStatus oldStatus = this->getSimulatorStatus();
+ m_simSimulating = false;
+ m_simulatingChangedTs = QDateTime::currentMSecsSinceEpoch();
+ this->emitSimulatorCombinedStatus(oldStatus);
+ }
+
+ void CSimulatorMsfs2024::onSimFrame()
+ {
+ if (m_updateRemoteAircraftInProgress) { return; }
+ QPointer myself(this);
+ QTimer::singleShot(0, this, [=] {
+ // run decoupled from simconnect event queue
+ if (!myself) { return; }
+ myself->updateRemoteAircraft();
+ });
+ }
+
+ void CSimulatorMsfs2024::onSimExit()
+ {
+ CLogMessage(this).info(u"Simulator exit: %1") << this->getSimulatorDetails();
+
+ // reset complete state, we are going down
+ m_simulatingChangedTs = QDateTime::currentMSecsSinceEpoch();
+ this->safeKillTimer();
+
+ // if called from dispatch function, avoid that SimConnectProc disconnects itself while in SimConnectProc
+ QPointer myself(this);
+ QTimer::singleShot(0, this, [=] {
+ if (!myself) { return; }
+ this->disconnectFrom();
+ });
+ }
+
+ SIMCONNECT_DATA_REQUEST_ID CSimulatorMsfs2024::obtainRequestIdForSimObjAircraft()
+ {
+ const SIMCONNECT_DATA_REQUEST_ID id = m_requestIdSimObjAircraft++;
+ if (id > RequestSimObjAircraftEnd) { m_requestIdSimObjAircraft = RequestSimObjAircraftStart; }
+ return id;
+ }
+
+ SIMCONNECT_DATA_REQUEST_ID CSimulatorMsfs2024::obtainRequestIdForSimObjTerrainProbe()
+ {
+ const SIMCONNECT_DATA_REQUEST_ID id = m_requestIdSimObjTerrainProbe++;
+ if (id > RequestSimObjTerrainProbeEnd) { m_requestIdSimObjTerrainProbe = RequestSimObjTerrainProbeStart; }
+ return id;
+ }
+
+ bool CSimulatorMsfs2024::releaseAIControl(const CSimConnectObject &simObject, SIMCONNECT_DATA_REQUEST_ID requestId)
+ {
+ const SIMCONNECT_OBJECT_ID objectId = simObject.getObjectId();
+ const HRESULT hr1 =
+ this->logAndTraceSendId(SimConnect_AIReleaseControl(m_hSimConnect, objectId, requestId), simObject,
+ "Release control", Q_FUNC_INFO, "SimConnect_AIReleaseControl");
+ const HRESULT hr2 =
+ this->logAndTraceSendId(SimConnect_TransmitClientEvent(m_hSimConnect, objectId, EventFreezeLatLng, 1,
+ SIMCONNECT_GROUP_PRIORITY_HIGHEST,
+ SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY),
+ simObject, "EventFreezeLatLng", Q_FUNC_INFO, "SimConnect_TransmitClientEvent");
+ const HRESULT hr3 =
+ this->logAndTraceSendId(SimConnect_TransmitClientEvent(m_hSimConnect, objectId, EventFreezeAlt, 1,
+ SIMCONNECT_GROUP_PRIORITY_HIGHEST,
+ SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY),
+ simObject, "EventFreezeAlt", Q_FUNC_INFO, "SimConnect_TransmitClientEvent");
+ const HRESULT hr4 =
+ this->logAndTraceSendId(SimConnect_TransmitClientEvent(m_hSimConnect, objectId, EventFreezeAtt, 1,
+ SIMCONNECT_GROUP_PRIORITY_HIGHEST,
+ SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY),
+ simObject, "EventFreezeAtt", Q_FUNC_INFO, "SimConnect_TransmitClientEvent");
+
+ return isOk(hr1, hr2, hr3, hr4);
+ }
+
+ bool CSimulatorMsfs2024::isValidSimObjectNotPendingRemoved(const CSimConnectObject &simObject) const
+ {
+ if (!simObject.hasValidRequestAndObjectId()) { return false; }
+ if (simObject.isPendingRemoved()) { return false; }
+ if (!m_simConnectObjects.contains(simObject.getCallsign())) { return false; } // removed in meantime
+ return true;
+ }
+
+ CSimConnectObject CSimulatorMsfs2024::getSimObjectForTrace(const TraceFsxSendId &trace) const
+ {
+ return m_simConnectObjects.getSimObjectForOtherSimObject(trace.simObject);
+ }
+
+ bool CSimulatorMsfs2024::removeSimObjectForTrace(const TraceFsxSendId &trace)
+ {
+ return m_simConnectObjects.removeByOtherSimObject(trace.simObject);
+ }
+
+ void CSimulatorMsfs2024::removeCamera(CSimConnectObject &simObject)
+ {
+ // not in FSX
+ Q_UNUSED(simObject)
+ }
+
+ void CSimulatorMsfs2024::removeObserver(CSimConnectObject &simObject)
+ {
+ // not in FSX
+ Q_UNUSED(simObject)
+ }
+
+ bool CSimulatorMsfs2024::triggerAutoTraceSendId(qint64 traceTimeMs)
+ {
+ if (m_traceSendId) { return false; } // no need
+ if (this->isShuttingDownOrDisconnected()) { return false; }
+ const qint64 ts = QDateTime::currentMSecsSinceEpoch();
+ const qint64 traceUntil = traceTimeMs + ts;
+ if (traceUntil <= m_traceAutoUntilTs) { return false; }
+ m_traceAutoUntilTs = traceUntil;
+
+ static const QString format("hh:mm:ss.zzz");
+ const QString untilString = QDateTime::fromMSecsSinceEpoch(traceUntil).toString(format);
+ CLogMessage(this).info(u"Triggered FSX/P3D auto trace until %1") << untilString;
+ const QPointer myself(this);
+ QTimer::singleShot(traceTimeMs * 1.2, this, [=] {
+ // triggered by mself (ts check), otherwise ignore
+ if (!myself) { return; }
+ if (m_traceAutoUntilTs > QDateTime::currentMSecsSinceEpoch()) { return; }
+ if (m_traceAutoUntilTs < 0) { return; } // alread off
+ CLogMessage(this).info(u"Auto trace id off");
+ m_traceAutoUntilTs = -1;
+ });
+ return true;
+ }
+
+ void CSimulatorMsfs2024::setTrueAltitude(CAircraftSituation &aircraftSituation,
+ const DataDefinitionOwnAircraft &simulatorOwnAircraft)
+ {
+ aircraftSituation.setAltitude(
+ CAltitude(simulatorOwnAircraft.altitudeFt, CAltitude::MeanSeaLevel, CLengthUnit::ft()));
+ }
+
+ void CSimulatorMsfs2024::updateOwnAircraftFromSimulator(const DataDefinitionOwnAircraft &simulatorOwnAircraft)
+ {
+ const qint64 ts = QDateTime::currentMSecsSinceEpoch();
+
+ CSimulatedAircraft myAircraft(getOwnAircraft());
+ CCoordinateGeodetic position;
+ position.setLatitude(CLatitude(simulatorOwnAircraft.latitudeDeg, CAngleUnit::deg()));
+ position.setLongitude(CLongitude(simulatorOwnAircraft.longitudeDeg, CAngleUnit::deg()));
+
+ if (simulatorOwnAircraft.pitchDeg < -90.0 || simulatorOwnAircraft.pitchDeg >= 90.0)
+ {
+ CLogMessage(this).warning(u"FSX: Pitch value (own aircraft) out of limits: %1")
+ << simulatorOwnAircraft.pitchDeg;
+ }
+ CAircraftSituation aircraftSituation;
+ aircraftSituation.setMSecsSinceEpoch(ts);
+ aircraftSituation.setPosition(position);
+ // MSFS has inverted pitch and bank angles
+ aircraftSituation.setPitch(CAngle(-simulatorOwnAircraft.pitchDeg, CAngleUnit::deg()));
+ aircraftSituation.setBank(CAngle(-simulatorOwnAircraft.bankDeg, CAngleUnit::deg()));
+ aircraftSituation.setHeading(CHeading(simulatorOwnAircraft.trueHeadingDeg, CHeading::True, CAngleUnit::deg()));
+ aircraftSituation.setGroundSpeed(CSpeed(simulatorOwnAircraft.velocity, CSpeedUnit::kts()));
+ aircraftSituation.setGroundElevation(
+ CAltitude(simulatorOwnAircraft.elevationFt, CAltitude::MeanSeaLevel, CLengthUnit::ft()),
+ CAircraftSituation::FromProvider);
+ setTrueAltitude(aircraftSituation, simulatorOwnAircraft);
+ aircraftSituation.setPressureAltitude(CAltitude(simulatorOwnAircraft.pressureAltitudeM, CAltitude::MeanSeaLevel,
+ CAltitude::PressureAltitude, CLengthUnit::m()));
+ // set on ground also in situation for consistency and future usage
+ // it is duplicated in parts
+ aircraftSituation.setOnGroundInfo(
+ { dtb(simulatorOwnAircraft.simOnGround) ? COnGroundInfo::OnGround : COnGroundInfo::NotOnGround,
+ COnGroundInfo::OutOnGroundOwnAircraft });
+
+ CAircraftVelocity aircraftVelocity(
+ simulatorOwnAircraft.velocityWorldX, simulatorOwnAircraft.velocityWorldY,
+ simulatorOwnAircraft.velocityWorldZ, CSpeedUnit::ft_s(), simulatorOwnAircraft.rotationVelocityBodyX,
+ simulatorOwnAircraft.rotationVelocityBodyZ, simulatorOwnAircraft.rotationVelocityBodyY, CAngleUnit::rad(),
+ CTimeUnit::s());
+ aircraftSituation.setVelocity(aircraftVelocity);
+
+ const CAircraftLights lights(dtb(simulatorOwnAircraft.lightStrobe), dtb(simulatorOwnAircraft.lightLanding),
+ dtb(simulatorOwnAircraft.lightTaxi), dtb(simulatorOwnAircraft.lightBeacon),
+ dtb(simulatorOwnAircraft.lightNav), dtb(simulatorOwnAircraft.lightLogo));
+
+ CAircraftEngineList engines;
+ const QList helperList { dtb(simulatorOwnAircraft.engine1Combustion),
+ dtb(simulatorOwnAircraft.engine2Combustion),
+ dtb(simulatorOwnAircraft.engine3Combustion),
+ dtb(simulatorOwnAircraft.engine4Combustion) };
+
+ for (int index = 0; index < simulatorOwnAircraft.numberOfEngines; ++index)
+ {
+ engines.push_back(CAircraftEngine(index + 1, helperList.value(index, true)));
+ }
+
+ const CAircraftParts parts(lights, dtb(simulatorOwnAircraft.gearHandlePosition),
+ qRound(simulatorOwnAircraft.flapsHandlePosition * 100),
+ dtb(simulatorOwnAircraft.spoilersHandlePosition), engines,
+ dtb(simulatorOwnAircraft.simOnGround), ts);
+
+ // set values
+ this->updateOwnSituationAndGroundElevation(aircraftSituation);
+ this->updateOwnParts(parts);
+
+ // When I change cockpit values in the sim (from GUI to simulator, not originating from simulator)
+ // it takes a little while before these values are set in the simulator.
+ // To avoid jitters, I wait some update cylces to stabilize the values
+ if (m_skipCockpitUpdateCycles < 1)
+ {
+ // defaults
+ CComSystem com1(myAircraft.getCom1System()); // set defaults
+ CComSystem com2(myAircraft.getCom2System());
+
+ // updates:
+ // https://www.fsdeveloper.com/forum/threads/com-unit-receiving-status-com-transmit-x-com-test-1-and-volume.445187/
+ // COM: If you're set to transmit on a unit, you WILL receive that unit.
+ // Otherwise if you're NOT set to transmit on a unit, then it will only receive if COM RECEIVE ALL is
+ // true. There is no control of COM volume.
+ com1.setFrequencyActive(CFrequency(simulatorOwnAircraft.com1ActiveMHz, CFrequencyUnit::MHz()));
+ com1.setFrequencyStandby(CFrequency(simulatorOwnAircraft.com1StandbyMHz, CFrequencyUnit::MHz()));
+ const bool comReceiveAll = dtb(simulatorOwnAircraft.comReceiveAll);
+ const bool com1Test = dtb(simulatorOwnAircraft.comTest1);
+ const bool com1Transmit = dtb(simulatorOwnAircraft.comTransmit1);
+ const int com1Status =
+ qRound(simulatorOwnAircraft.comStatus1); // Radio status flag : -1 =Invalid 0 = OK 1 =
+ // Does not exist 2 = No electricity 3 = Failed
+ com1.setTransmitEnabled(com1Status == 0 && com1Transmit);
+ com1.setReceiveEnabled(com1Status == 0 && (comReceiveAll || com1Transmit));
+
+ const bool changedCom1Active =
+ myAircraft.getCom1System().getFrequencyActive() != com1.getFrequencyActive() &&
+ com1.getFrequencyActive() != m_lastCom1Active;
+ const bool changedCom1Standby =
+ myAircraft.getCom1System().getFrequencyStandby() != com1.getFrequencyStandby() &&
+ com1.getFrequencyStandby() != m_lastCom1Standby;
+
+ // Avoid overwrite of 8.33 kHz frequency with data from simulator
+ if (!changedCom1Active) { com1.setFrequencyActive(myAircraft.getCom1System().getFrequencyActive()); }
+ else { m_lastCom1Active.setNull(); }
+
+ if (!changedCom1Standby) { com1.setFrequencyStandby(myAircraft.getCom1System().getFrequencyStandby()); }
+ else { m_lastCom1Standby.setNull(); }
+
+ const bool changedCom1 = myAircraft.getCom1System() != com1;
+
+ m_simCom1 = com1;
+ Q_UNUSED(com1Test)
+
+ com2.setFrequencyActive(CFrequency(simulatorOwnAircraft.com2ActiveMHz, CFrequencyUnit::MHz()));
+ com2.setFrequencyStandby(CFrequency(simulatorOwnAircraft.com2StandbyMHz, CFrequencyUnit::MHz()));
+ const bool com2Test = dtb(simulatorOwnAircraft.comTest2);
+ const bool com2Transmit = dtb(simulatorOwnAircraft.comTransmit2);
+ const int com2Status =
+ qRound(simulatorOwnAircraft.comStatus2); // Radio status flag : -1 =Invalid 0 = OK 1 =
+ // Does not exist 2 = No electricity 3 = Failed
+ com2.setTransmitEnabled(com2Status == 0 && com2Transmit);
+ com2.setReceiveEnabled(com2Status == 0 && (comReceiveAll || com2Transmit));
+ const bool changedCom2Active =
+ myAircraft.getCom2System().getFrequencyActive() != com2.getFrequencyActive() &&
+ com2.getFrequencyActive() != m_lastCom2Active;
+ const bool changedCom2Standby =
+ myAircraft.getCom2System().getFrequencyStandby() != com2.getFrequencyStandby() &&
+ com2.getFrequencyStandby() != m_lastCom2Standby;
+
+ // Avoid overwrite of 8.33 kHz frequency with data from simulator
+ if (!changedCom2Active) { com2.setFrequencyActive(myAircraft.getCom2System().getFrequencyActive()); }
+ else { m_lastCom2Active.setNull(); }
+
+ if (!changedCom2Standby) { com2.setFrequencyStandby(myAircraft.getCom2System().getFrequencyStandby()); }
+ else { m_lastCom2Standby.setNull(); }
+
+ const bool changedCom2 = myAircraft.getCom2System() != com2;
+
+ m_simCom2 = com2;
+ Q_UNUSED(com2Test)
+
+ CTransponder transponder(myAircraft.getTransponder());
+ transponder.setTransponderCode(qRound(simulatorOwnAircraft.transponderCode));
+ m_simTransponder = transponder;
+
+ // if the simulator ever sends SELCAL, add it here.
+ // m_selcal SELCAL sync.would go here
+
+ const bool changedXpr = (myAircraft.getTransponderCode() != transponder.getTransponderCode());
+
+ if (changedCom1 || changedCom2 || changedXpr)
+ {
+ // set in own aircraft provider
+ this->updateCockpit(com1, com2, transponder, identifier());
+ }
+ }
+ else { --m_skipCockpitUpdateCycles; }
+
+ // slower updates
+ if (m_ownAircraftUpdateCycles % 10 == 0)
+ {
+ // init terrain probes here has the advantage we can also switch it on/off at runtime
+ if (m_useFsxTerrainProbe && !m_initFsxTerrainProbes)
+ {
+ this->physicallyInitAITerrainProbes(position, 2); // init probe
+ }
+
+ // SB3 offsets updating
+ m_simulatorInternals.setValue(QStringLiteral("fsx/sb3"), boolToEnabledDisabled(m_useSbOffsets));
+ m_simulatorInternals.setValue(QStringLiteral("fsx/sb3packets"), m_useSbOffsets ?
+ QString::number(m_sbDataReceived) :
+ QStringLiteral("disabled"));
+
+ // CG
+ const CLength cg(simulatorOwnAircraft.cgToGroundFt, CLengthUnit::ft());
+ this->updateOwnCG(cg);
+
+ // Simulated objects instead of NON ATC
+ m_simulatorInternals.setValue(QStringLiteral("fsx/addAsSimulatedObject"),
+ boolToEnabledDisabled(m_useAddSimulatedObj));
+
+ } // slow updates
+
+ m_ownAircraftUpdateCycles++; // with 50 updates/sec long enough even for 32bit
+ }
+
+ void CSimulatorMsfs2024::triggerUpdateRemoteAircraftFromSimulator(const CSimConnectObject &simObject,
+ const DataDefinitionPosData &remoteAircraftData)
+ {
+ if (this->isShuttingDownOrDisconnected()) { return; }
+ QPointer myself(this);
+ QTimer::singleShot(0, this, [=] {
+ if (!myself) { return; }
+ myself->updateRemoteAircraftFromSimulator(simObject, remoteAircraftData);
+ });
+ }
+
+ void CSimulatorMsfs2024::triggerUpdateRemoteAircraftFromSimulator(
+ const CSimConnectObject &simObject, const DataDefinitionRemoteAircraftModel &remoteAircraftModel)
+ {
+ if (this->isShuttingDownOrDisconnected()) { return; }
+ QPointer myself(this);
+ QTimer::singleShot(0, this, [=] {
+ if (!myself) { return; }
+ myself->updateRemoteAircraftFromSimulator(simObject, remoteAircraftModel);
+ });
+ }
+
+ void CSimulatorMsfs2024::updateRemoteAircraftFromSimulator(const CSimConnectObject &simObject,
+ const DataDefinitionPosData &remoteAircraftData)
+ {
+ if (this->isShuttingDownOrDisconnected()) { return; }
+
+ // Near ground we use faster updates
+ const CCallsign cs(simObject.getCallsign());
+ CAircraftSituation lastSituation = m_lastSentSituations[cs];
+ const bool moving = lastSituation.isMoving();
+ const bool onGround = remoteAircraftData.isOnGround();
+
+ // CElevationPlane: deg, deg, feet
+ // we only remember near ground
+ const CElevationPlane elevation =
+ CElevationPlane(remoteAircraftData.latitudeDeg, remoteAircraftData.longitudeDeg,
+ remoteAircraftData.elevationFt, CElevationPlane::singlePointRadius());
+ if (remoteAircraftData.aboveGroundFt() < 250)
+ {
+ const CLength cg(remoteAircraftData.cgToGroundFt, CLengthUnit::ft());
+ this->rememberElevationAndSimulatorCG(cs, simObject.getAircraftModel(), onGround, elevation, cg);
+ }
+
+ const bool log = this->isLogCallsign(cs);
+ if (log)
+ {
+ // update lat/lng/alt with real data from sim
+ const CAltitude alt(remoteAircraftData.altitudeFt, CAltitude::MeanSeaLevel, CAltitude::TrueAltitude,
+ CLengthUnit::ft());
+ lastSituation.setPosition(elevation);
+ lastSituation.setAltitude(alt);
+ lastSituation.setGroundElevation(elevation, CAircraftSituation::FromProvider);
+ this->addLoopbackSituation(lastSituation);
+ }
+
+ if (moving && remoteAircraftData.aboveGroundFt() <= 100.0)
+ {
+ // switch to fast updates
+ if (simObject.getSimDataPeriod() != SIMCONNECT_PERIOD_VISUAL_FRAME)
+ {
+ this->requestPositionDataForSimObject(simObject, SIMCONNECT_PERIOD_VISUAL_FRAME);
+ }
+ }
+ else
+ {
+ // switch to slow updates
+ if (simObject.getSimDataPeriod() != SIMCONNECT_PERIOD_SECOND)
+ {
+ this->requestPositionDataForSimObject(simObject, SIMCONNECT_PERIOD_SECOND);
+ }
+ }
+ }
+
+ void
+ CSimulatorMsfs2024::updateRemoteAircraftFromSimulator(const CSimConnectObject &simObject,
+ const DataDefinitionRemoteAircraftModel &remoteAircraftModel)
+ {
+ const CCallsign cs(simObject.getCallsign());
+ if (!m_simConnectObjects.contains(cs)) { return; } // no longer existing
+ CSimConnectObject &so = m_simConnectObjects[cs];
+ if (so.isPendingRemoved()) { return; }
+
+ const QString modelString(remoteAircraftModel.title);
+ const CLength cg(remoteAircraftModel.cgToGroundFt, CLengthUnit::ft());
+ so.setAircraftCG(cg);
+ so.setAircraftModelString(modelString);
+
+ // update in 2 providers
+ this->rememberElevationAndSimulatorCG(cs, simObject.getAircraftModel(), false, CElevationPlane::null(),
+ cg); // env. provider
+ this->updateCGAndModelString(cs, cg, modelString); // remote aircraft provider
+ }
+
+ void CSimulatorMsfs2024::updateProbeFromSimulator(const CCallsign &callsign,
+ const DataDefinitionPosData &remoteAircraftData)
+ {
+ const CElevationPlane elevation(remoteAircraftData.latitudeDeg, remoteAircraftData.longitudeDeg,
+ remoteAircraftData.elevationFt, CElevationPlane::singlePointRadius());
+ this->callbackReceivedRequestedElevation(elevation, callsign, false);
+ }
+
+ void CSimulatorMsfs2024::updateOwnAircraftFromSimulator(const DataDefinitionClientAreaSb &sbDataArea)
+ {
+ if (m_skipCockpitUpdateCycles > 0) { return; }
+
+ // log SB offset
+ if (m_logSbOffsets) { CLogMessage(this).info(u"SB from sim: " % sbDataArea.toQString()); }
+
+ // SB XPDR mode
+ CTransponder::TransponderMode newMode = CTransponder::StateIdent;
+ if (!sbDataArea.isIdent())
+ {
+ newMode = sbDataArea.isStandby() ? CTransponder::StateStandby : CTransponder::ModeC;
+ }
+ const CSimulatedAircraft myAircraft(this->getOwnAircraft());
+ const bool changed = (myAircraft.getTransponderMode() != newMode);
+ if (!changed) { return; }
+ CTransponder xpdr = myAircraft.getTransponder();
+ xpdr.setTransponderMode(newMode);
+ this->updateCockpit(myAircraft.getCom1System(), myAircraft.getCom2System(), xpdr, this->identifier());
+ }
+
+ void CSimulatorMsfs2024::updateTransponderMode(const CTransponder::TransponderMode xpdrMode)
+ {
+ if (m_skipCockpitUpdateCycles > 0) { return; }
+ const CSimulatedAircraft myAircraft(this->getOwnAircraft());
+ const bool changed = (myAircraft.getTransponderMode() != xpdrMode);
+ if (!changed) { return; }
+ CTransponder myXpdr = myAircraft.getTransponder();
+ myXpdr.setTransponderMode(xpdrMode);
+ this->updateCockpit(myAircraft.getCom1System(), myAircraft.getCom2System(), myXpdr, this->identifier());
+ }
+
+ void CSimulatorMsfs2024::updateMSFSTransponderMode(const DataDefinitionMSFSTransponderMode transponderMode)
+ {
+ auto mode = CTransponder::StateIdent;
+ if (!transponderMode.ident)
+ {
+ qRound(transponderMode.transponderMode) >= 3 ? mode = CTransponder::ModeC :
+ mode = CTransponder::StateStandby;
+ }
+ this->updateTransponderMode(mode);
+ }
+
+ bool CSimulatorMsfs2024::simulatorReportedObjectAdded(DWORD objectId)
+ {
+ if (this->isShuttingDownOrDisconnected()) { return true; } // pretend everything is fine
+ const CSimConnectObject simObject = m_simConnectObjects.getSimObjectForObjectId(objectId);
+ const CCallsign callsign(simObject.getCallsign());
+ if (!simObject.hasValidRequestAndObjectId() || callsign.isEmpty()) { return false; }
+
+ // we know the object has been created. But it can happen it is directly removed afterwards
+ const CSimulatedAircraft verifyAircraft(simObject.getAircraft());
+ const QPointer myself(this);
+ QTimer::singleShot(1000, this, [=] {
+ // verify aircraft and also triggers new add if required
+ // do not do this in the event loop, so we do this deferred
+ if (!myself || this->isShuttingDownOrDisconnected()) { return; }
+ this->verifyAddedRemoteAircraft(verifyAircraft);
+ });
+ return true;
+ }
+
+ void CSimulatorMsfs2024::verifyAddedRemoteAircraft(const CSimulatedAircraft &remoteAircraftIn)
+ {
+ if (this->isShuttingDownOrDisconnected()) { return; }
+ if (remoteAircraftIn.isTerrainProbe())
+ {
+ this->verifyAddedTerrainProbe(remoteAircraftIn);
+ return;
+ }
+
+ CStatusMessage msg;
+ CSimulatedAircraft remoteAircraft = remoteAircraftIn;
+ const CCallsign callsign(remoteAircraft.getCallsign());
+
+ do {
+ // no callsign
+ if (callsign.isEmpty())
+ {
+ msg = CLogMessage(this).error(u"Cannot confirm AI object, empty callsign");
+ break;
+ }
+
+ // removed in meantime
+ const bool aircraftStillInRange = this->isAircraftInRange(callsign);
+ if (!m_simConnectObjects.contains(callsign))
+ {
+ if (aircraftStillInRange)
+ {
+ msg = CLogMessage(this).warning(
+ u"Callsign '%1' removed in meantime from AI objects, but still in range")
+ << callsign.toQString();
+ }
+ else
+ {
+ this->removeFromAddPendingAndAddAgainAircraft(callsign);
+ msg = CLogMessage(this).info(u"Callsign '%1' removed in meantime and no longer in range")
+ << callsign.toQString();
+ }
+ break;
+ }
+
+ CSimConnectObject &simObject = m_simConnectObjects[callsign];
+ remoteAircraft = simObject.getAircraft(); // update, if something has changed
+
+ if (!simObject.hasValidRequestAndObjectId() || simObject.isPendingRemoved())
+ {
+ msg = CStatusMessage(this).warning(u"Object for callsign '%1'/id: %2 removed in meantime/invalid")
+ << callsign.toQString() << simObject.getObjectId();
+ break;
+ }
+
+ // P3D also has SimConnect_AIReleaseControlEx which also allows to destroy the aircraft
+ const SIMCONNECT_DATA_REQUEST_ID requestReleaseId = this->obtainRequestIdForSimObjAircraft();
+ const bool released = this->releaseAIControl(simObject, requestReleaseId);
+
+ if (!released)
+ {
+ msg = CStatusMessage(this).error(u"Cannot confirm model '%1' %2")
+ << remoteAircraft.getModelString() << simObject.toQString();
+ break;
+ }
+
+ // confirm as added, this is also required to request light, etc
+ Q_ASSERT_X(simObject.isPendingAdded(), Q_FUNC_INFO, "Already confirmed, this should be the only place");
+ simObject.setConfirmedAdded(true); // aircraft
+
+ // request data on object
+ this->requestPositionDataForSimObject(simObject);
+ this->requestLightsForSimObject(simObject);
+ this->requestModelInfoForSimObject(simObject);
+
+ this->removeFromAddPendingAndAddAgainAircraft(callsign); // no longer try to add
+ const bool updated = this->updateAircraftRendered(callsign, true);
+ if (updated)
+ {
+ static const QString debugMsg("CS: '%1' model: '%2' verified, request/object id: %3 %4");
+ if (this->showDebugLogMessage())
+ {
+ this->debugLogMessage(Q_FUNC_INFO,
+ debugMsg.arg(callsign.toQString(), remoteAircraft.getModelString())
+ .arg(simObject.getRequestId())
+ .arg(simObject.getObjectId()));
+ }
+
+ this->sendRemoteAircraftAtcDataToSimulator(simObject);
+ emit this->aircraftRenderingChanged(simObject.getAircraft());
+ }
+ else
+ {
+ CLogMessage(this).warning(
+ u"Verified aircraft '%1' model '%2', request/object id: %3 %4 was already marked rendered")
+ << callsign.asString() << remoteAircraft.getModelString() << simObject.getRequestId()
+ << simObject.getObjectId();
+ }
+
+ if (simObject.isConfirmedAdded() && simObject.getType() == CSimConnectObject::AircraftSimulatedObject)
+ {
+ CLogMessage(this).warning(u"Confirm added model '%1' '%2', but as '%3'")
+ << remoteAircraft.getCallsignAsString() << remoteAircraft.getModelString()
+ << simObject.getTypeAsString();
+ this->triggerAutoTraceSendId(); // trace for some time (issues regarding this workaround?)
+ simObject.decreaseAddingExceptions(); // if previously increased and now working, reset
+ }
+ }
+ while (false);
+
+ // log errors and emit signal
+ if (!msg.isEmpty() && msg.isWarningOrAbove())
+ {
+ CLogMessage::preformatted(msg);
+ emit this->physicallyAddingRemoteModelFailed(CSimulatedAircraft(), false, false, msg);
+ }
+
+ // trigger adding pending aircraft if there are any
+ if (!m_addPendingAircraft.isEmpty()) { this->addPendingAircraftAfterAdded(); }
+ }
+
+ void CSimulatorMsfs2024::addingAircraftFailed(const CSimConnectObject &simObject)
+ {
+ if (CBuildConfig::isLocalDeveloperDebugBuild())
+ {
+ Q_ASSERT_X(simObject.isAircraft(), Q_FUNC_INFO, "Need aircraft");
+ }
+ if (!simObject.isAircraft()) { return; }
+
+ // clean up
+ m_simConnectObjects.removeByOtherSimObject(simObject);
+ this->removeFromAddPendingAndAddAgainAircraft(simObject.getCallsign());
+
+ CLogMessage(this).warning(u"Model failed to be added: '%1' details: %2")
+ << simObject.getAircraftModelString() << simObject.getAircraft().toQString(true);
+ CStatusMessage verifyMsg;
+ const bool verifiedAircraft = this->verifyFailedAircraftInfo(simObject, verifyMsg); // aircraft.cfg existing?
+ if (!verifyMsg.isEmpty()) { CLogMessage::preformatted(verifyMsg); }
+
+ CSimConnectObject simObjAddAgain(simObject);
+ simObjAddAgain.increaseAddingExceptions();
+ if (!simObject.hasCallsign())
+ {
+ SWIFT_VERIFY_X(false, Q_FUNC_INFO, "Missing callsign");
+ return;
+ }
+
+ if (!verifiedAircraft || simObjAddAgain.getAddingExceptions() > ThresholdAddException)
+ {
+ const CStatusMessage msg =
+ verifiedAircraft ?
+ CLogMessage(this).warning(u"Model '%1' %2 failed %3 time(s) before and will be disabled")
+ << simObjAddAgain.getAircraftModelString() << simObjAddAgain.toQString()
+ << simObjAddAgain.getAddingExceptions() :
+ CLogMessage(this).warning(u"Model '%1' %2 failed verification and will be disabled")
+ << simObjAddAgain.getAircraftModelString() << simObjAddAgain.toQString();
+ this->updateAircraftEnabled(simObjAddAgain.getCallsign(), false); // disable
+ emit this->physicallyAddingRemoteModelFailed(simObjAddAgain.getAircraft(), true, true,
+ msg); // verify failed
+ }
+ else
+ {
+ CLogMessage(this).info(u"Will try '%1' again, aircraft: %2")
+ << simObject.getAircraftModelString() << simObject.getAircraft().toQString(true);
+ QPointer myself(this);
+ QTimer::singleShot(2000, this, [=] {
+ if (!myself) { return; }
+ if (this->isShuttingDownOrDisconnected()) { return; }
+ m_addPendingAircraft.insert(simObjAddAgain, true); // add failed object
+ });
+ }
+ }
+
+ bool CSimulatorMsfs2024::verifyFailedAircraftInfo(const CSimConnectObject &simObject, CStatusMessage &details) const
+ {
+ CAircraftModel model = simObject.getAircraftModel();
+
+ const CSpecializedSimulatorSettings settings = this->getSimulatorSettings();
+ const bool fileExists = CFsCommonUtil::adjustFileDirectory(model, settings.getModelDirectoriesOrDefault());
+ bool canBeUsed = true;
+
+ CStatusMessageList messages;
+ if (fileExists)
+ {
+ // we can access the aircraft.cfg file
+ bool parsed = false;
+ const CAircraftCfgEntriesList entries =
+ CAircraftCfgParser::performParsingOfSingleFile(model.getFileName(), parsed, messages);
+ if (parsed)
+ {
+ if (entries.containsTitle(model.getModelString()))
+ {
+ messages.push_back(CStatusMessage(this).info(u"Model '%1' exists in re-parsed file '%2'.")
+ << model.getModelString() << model.getFileName());
+ canBeUsed = true; // all OK
+ }
+ else
+ {
+ messages.push_back(
+ CStatusMessage(this).warning(u"Model '%1' no longer in re-parsed file '%2'. Models are: %3.")
+ << model.getModelString() << model.getFileName() << entries.getTitlesAsString(true));
+ canBeUsed = false; // absolute no chance to use that one
+ }
+ }
+ else
+ {
+ messages.push_back(CStatusMessage(this).warning(u"CS: '%1' Cannot parse file: '%2' (existing: %3)")
+ << model.getCallsign().asString() << model.getFileName()
+ << boolToYesNo(model.hasExistingCorrespondingFile()));
+ }
+ }
+ else
+ {
+ // the file cannot be accessed right now, but the pilot client necessarily has access to them
+ // so we just carry on
+ messages = model.verifyModelData();
+ }
+
+ // as single message
+ details = messages.toSingleMessage();
+
+ // status
+ return canBeUsed;
+ }
+
+ bool CSimulatorMsfs2024::logVerifyFailedAircraftInfo(const CSimConnectObject &simObject) const
+ {
+ CStatusMessage m;
+ const bool r = verifyFailedAircraftInfo(simObject, m);
+ if (!m.isEmpty()) { CLogMessage::preformatted(m); }
+ return r;
+ }
+
+ void CSimulatorMsfs2024::verifyAddedTerrainProbe(const CSimulatedAircraft &remoteAircraftIn)
+ {
+ bool verified = false;
+ CCallsign cs;
+
+ // no simObject reference outside that block, because it will be deleted
+ {
+ CSimConnectObject &simObject = m_simConnectObjects[remoteAircraftIn.getCallsign()];
+ simObject.setConfirmedAdded(true); // terrain probe
+ simObject.resetTimestampToNow();
+ cs = simObject.getCallsign();
+ CLogMessage(this).info(u"Probe: '%1' '%2' confirmed, %3")
+ << simObject.getCallsignAsString() << simObject.getAircraftModelString() << simObject.toQString();
+
+ // fails for probe
+ // SIMCONNECT_DATA_REQUEST_ID requestId = this->obtainRequestIdForSimObjTerrainProbe();
+ // verified = this->releaseAIControl(simObject, requestId); // release probe
+ verified = true;
+ }
+
+ if (!verified) // cppcheck-suppress knownConditionTrueFalse
+ {
+ CLogMessage(this).info(u"Disable probes: '%1' failed to relase control") << cs.asString();
+ m_useFsxTerrainProbe = false;
+ }
+
+ // trigger new adding from pending if any
+ if (!m_addPendingAircraft.isEmpty()) { this->addPendingAircraftAfterAdded(); }
+ }
+
+ void CSimulatorMsfs2024::timerBasedObjectAddOrRemove()
+ {
+ this->addPendingAircraft(AddByTimer);
+ if (!this->isTestMode()) { this->physicallyRemoveAircraftNotInProvider(); }
+ }
+
+ void CSimulatorMsfs2024::addPendingAircraftAfterAdded()
+ {
+ this->addPendingAircraft(AddAfterAdded); // addPendingAircraft is already "non blocking"
+ }
+
+ void CSimulatorMsfs2024::addPendingAircraft(AircraftAddMode mode)
+ {
+ if (m_addPendingAircraft.isEmpty()) { return; }
+ const CCallsignSet aircraftCallsignsInRange(this->getAircraftInRangeCallsigns());
+ CSimulatedAircraftList toBeAddedAircraft; // aircraft still to be added
+ CCallsignSet toBeRemovedCallsigns;
+
+ for (const CSimConnectObject &pendingSimObj : std::as_const(m_addPendingAircraft))
+ {
+ SWIFT_VERIFY_X(pendingSimObj.hasCallsign(), Q_FUNC_INFO, "missing callsign");
+ if (!pendingSimObj.hasCallsign()) { continue; }
+ if (pendingSimObj.isTerrainProbe() || aircraftCallsignsInRange.contains(pendingSimObj.getCallsign()))
+ {
+ toBeAddedAircraft.push_back(pendingSimObj.getAircraft());
+ }
+ else { toBeRemovedCallsigns.push_back(pendingSimObj.getCallsign()); }
+ }
+
+ // no longer required to be added
+ m_addPendingAircraft.removeCallsigns(toBeRemovedCallsigns);
+ m_addAgainAircraftWhenRemoved.removeByCallsigns(toBeRemovedCallsigns);
+
+ // add aircraft, but "non blocking"
+ if (!toBeAddedAircraft.isEmpty())
+ {
+ const CSimConnectObject oldestSimObject = m_addPendingAircraft.getOldestObject();
+ const CSimulatedAircraft nextPendingAircraft = oldestSimObject.getAircraft();
+ if (nextPendingAircraft.hasModelString())
+ {
+ const QPointer myself(this);
+ QTimer::singleShot(100, this, [=] {
+ if (!myself) { return; }
+ if (this->isShuttingDownDisconnectedOrNoAircraft(nextPendingAircraft.isTerrainProbe())) { return; }
+ this->physicallyAddRemoteAircraftImpl(nextPendingAircraft, mode, oldestSimObject);
+ });
+ }
+ else
+ {
+ CLogMessage(this).warning(u"Pending aircraft without model string will be removed");
+ m_addPendingAircraft.removeByOtherSimObject(oldestSimObject);
+ }
+ }
+ }
+
+ CSimConnectObject CSimulatorMsfs2024::removeFromAddPendingAndAddAgainAircraft(const CCallsign &callsign)
+ {
+ CSimConnectObject simObjectOld;
+ if (callsign.isEmpty()) { return simObjectOld; }
+
+ m_addAgainAircraftWhenRemoved.removeByCallsign(callsign);
+ if (m_addPendingAircraft.contains(callsign))
+ {
+ simObjectOld = m_addPendingAircraft[callsign];
+ m_addPendingAircraft.remove(callsign);
+ }
+ return simObjectOld;
+ }
+
+ bool CSimulatorMsfs2024::simulatorReportedObjectRemoved(DWORD objectID)
+ {
+ if (this->isShuttingDownOrDisconnected()) { return false; }
+ CSimConnectObject simObject = m_simConnectObjects.getSimObjectForObjectId(objectID);
+ if (!simObject.hasValidRequestAndObjectId()) { return false; } // object id from somewhere else
+
+ const CCallsign callsign(simObject.getCallsign());
+ Q_ASSERT_X(!callsign.isEmpty(), Q_FUNC_INFO, "Missing callsign for removed object");
+
+ if (simObject.isPendingRemoved())
+ {
+ // good case, object has been removed
+ // we can remove the simulator object
+ }
+ else
+ {
+ // object was removed, but removal was not requested by us
+ // this means we are out of the reality bubble or something else went wrong
+ // Possible reasons:
+ // 1) out of reality bubble, because we move to another airport or other reasons
+ // 2) wrong position (in ground etc.)
+ // 3) Simulator not running (ie in stopped mode)
+ CStatusMessage msg;
+ if (!simObject.getAircraftModelString().isEmpty() &&
+ simObject.getAddingDirectlyRemoved() < ThresholdAddedAndDirectlyRemoved)
+ {
+ simObject.increaseAddingDirectlyRemoved();
+ m_addPendingAircraft.insert(simObject, true); // insert removed objects and update ts
+ m_simConnectObjects.removeByOtherSimObject(
+ simObject); // we have it in pending now, no need to keep it in this list
+
+ const CInterpolationAndRenderingSetupPerCallsign setup =
+ this->getInterpolationSetupPerCallsignOrDefault(callsign);
+ msg = CLogMessage(this).warning(u"Aircraft removed, '%1' '%2' object id '%3' out of reality bubble or "
+ u"other reason. Interpolator: '%4'")
+ << callsign.toQString() << simObject.getAircraftModelString() << objectID
+ << simObject.getInterpolatorInfo(setup.getInterpolatorMode());
+ }
+ else if (simObject.getAddingDirectlyRemoved() < ThresholdAddedAndDirectlyRemoved)
+ {
+ const CStatusMessage m =
+ CLogMessage(this).warning(
+ u"Aircraft removed again multiple times and will be disabled, '%1' '%2' object id '%3'")
+ << callsign.toQString() << simObject.getAircraftModelString() << objectID;
+ this->updateAircraftEnabled(simObject.getCallsign(), false);
+ emit this->physicallyAddingRemoteModelFailed(simObject.getAircraft(), true, true,
+ m); // directly removed again
+ }
+ else
+ {
+ msg = CLogMessage(this).warning(
+ u"Removed '%1' from simulator, but was not initiated by us (swift): %1 '%2' object id %3")
+ << callsign.toQString() << simObject.getAircraftModelString() << objectID;
+ }
+
+ // in all cases add verification details
+ this->logVerifyFailedAircraftInfo(simObject);
+
+ // relay messages
+ if (!msg.isEmpty()) { emit this->driverMessages(msg); }
+ }
+
+ // in all cases we remove the object
+ const int c = m_simConnectObjects.remove(callsign);
+ const bool removedAny = (c > 0);
+ const bool updated = this->updateAircraftRendered(simObject.getCallsign(), false);
+ if (updated) { emit this->aircraftRenderingChanged(simObject.getAircraft()); }
+
+ // models we have to add again after removing
+ if (m_addAgainAircraftWhenRemoved.containsCallsign(callsign))
+ {
+ const CSimulatedAircraft aircraftAddAgain = m_addAgainAircraftWhenRemoved.findFirstByCallsign(callsign);
+ m_addAgainAircraftWhenRemoved.removeByCallsign(callsign);
+ QPointer myself(this);
+ QTimer::singleShot(2500, this, [=] {
+ if (!myself) { return; }
+ if (this->isShuttingDownOrDisconnected()) { return; }
+ myself->physicallyAddRemoteAircraftImpl(aircraftAddAgain, AddedAfterRemoved);
+ });
+ }
+ return removedAny;
+ }
+
+ bool CSimulatorMsfs2024::setSimConnectObjectId(DWORD requestId, DWORD objectId)
+ {
+ return m_simConnectObjects.setSimConnectObjectIdForRequestId(requestId, objectId);
+ }
+
+ bool CSimulatorMsfs2024::setCurrentLights(const CCallsign &callsign, const CAircraftLights &lights)
+ {
+ if (!m_simConnectObjects.contains(callsign)) { return false; }
+ m_simConnectObjects[callsign].setCurrentLightsInSimulator(lights);
+ return true;
+ }
+
+ bool CSimulatorMsfs2024::setLightsAsSent(const CCallsign &callsign, const CAircraftLights &lights)
+ {
+ if (!m_simConnectObjects.contains(callsign)) { return false; }
+ m_simConnectObjects[callsign].setLightsAsSent(lights);
+ return true;
+ }
+
+ void CSimulatorMsfs2024::timerEvent(QTimerEvent *event)
+ {
+ Q_UNUSED(event)
+ if (this->isShuttingDown()) { return; }
+ this->dispatch();
+ }
+
+ HRESULT CSimulatorMsfs2024::initEventsP3D() { return s_ok(); }
+
+ bool CSimulatorMsfs2024::parseDetails(const CSimpleCommandParser &parser)
+ {
+ // .driver sendid on|off
+ if (parser.matchesPart(1, "sendid") && parser.hasPart(2))
+ {
+ const bool trace = parser.toBool(2);
+ this->setTraceSendId(trace);
+ CLogMessage(this, CLogCategories::cmdLine()).info(u"Tracing %1 driver sendIds is '%2'")
+ << this->getSimulatorPluginInfo().getIdentifier() << boolToOnOff(trace);
+ return true;
+ }
+
+ // .driver sboffsets on|off
+ if (parser.matchesPart(1, "sboffsets") && parser.hasPart(2))
+ {
+ const bool on = parser.toBool(2);
+ this->setUsingSbOffsetValues(on);
+ CLogMessage(this, CLogCategories::cmdLine()).info(u"SB offsets is '%1'") << boolToOnOff(on);
+ return true;
+ }
+
+ // .driver sblog on|off
+ if (parser.matchesPart(1, "sblog") && parser.hasPart(2))
+ {
+ const bool on = parser.toBool(2);
+ m_logSbOffsets = on;
+ CLogMessage(this, CLogCategories::cmdLine()).info(u"SB log. offsets is '%1'") << boolToOnOff(on);
+ return true;
+ }
+
+ return CSimulatorFsCommon::parseDetails(parser);
+ }
+
+ void CSimulatorMsfs2024::registerHelp()
+ {
+ if (CSimpleCommandParser::registered(
+ "swift::simplugin::msfs2024common::CSimulatorMsfs2024::CSimulatorMsfs2024"))
+ {
+ return;
+ }
+ CSimpleCommandParser::registerCommand({ ".drv", "alias: .driver .plugin" });
+ CSimpleCommandParser::registerCommand({ ".drv sendid on|off", "Trace simConnect sendId on|off" });
+ CSimpleCommandParser::registerCommand({ ".drv sboffsets on|off", "SB offsets via simConnect on|off" });
+ CSimpleCommandParser::registerCommand({ ".drv sblog on|off", "SB offsets logging on|off" });
+ }
+
+ CCallsign CSimulatorMsfs2024::getCallsignForPendingProbeRequests(DWORD requestId, bool remove)
+ {
+ const CCallsign cs = m_pendingProbeRequests.value(requestId);
+ if (remove) { m_pendingProbeRequests.remove(requestId); }
+ return cs;
+ }
+
+ const QString &CSimulatorMsfs2024::modeToString(CSimulatorMsfs2024::AircraftAddMode mode)
+ {
+ static const QString e("external call");
+ static const QString pt("add pending by timer");
+ static const QString oa("add pending after object added");
+ static const QString ar("add again after removed");
+ static const QString dontKnow("???");
+
+ switch (mode)
+ {
+ case ExternalCall: return e;
+ case AddByTimer: return pt;
+ case AddAfterAdded: return oa;
+ case AddedAfterRemoved: return ar;
+ default: break;
+ }
+ return dontKnow;
+ }
+
+ void CSimulatorMsfs2024::dispatch()
+ {
+ // call CSimulatorMsfs2024::SimConnectProc
+ Q_ASSERT_X(m_dispatchProc, Q_FUNC_INFO, "Missing DispatchProc");
+
+ // statistics
+ m_dispatchReceiveIdLast = SIMCONNECT_RECV_ID_NULL;
+ m_dispatchRequestIdLast = CSimConnectDefinitions::RequestEndMarker;
+ const qint64 start = QDateTime::currentMSecsSinceEpoch();
+
+ // process
+ const HRESULT hr = SimConnect_CallDispatch(m_hSimConnect, m_dispatchProc, this);
+
+ // statistics
+ const qint64 end = QDateTime::currentMSecsSinceEpoch();
+ m_dispatchTimeMs = end - start;
+ if (m_dispatchMaxTimeMs < m_dispatchTimeMs)
+ {
+ m_dispatchMaxTimeMs = m_dispatchTimeMs;
+ m_dispatchReceiveIdMaxTime = m_dispatchReceiveIdLast;
+ m_dispatchRequestIdMaxTime = m_dispatchRequestIdLast;
+ }
+
+ // error handling
+ if (isFailure(hr))
+ {
+ // on FSX we normally receive this one here when simulator goes down, and NOT onSimExit
+ // in that case sim status is Connected, but not PAUSED or SIMULATING
+ const SimulatorStatus simStatus = this->getSimulatorStatus();
+ const bool disconnectedOrNotSimulating =
+ simStatus.testFlag(Disconnected) || !simStatus.testFlag(Simulating);
+
+ m_dispatchErrors++;
+ this->triggerAutoTraceSendId();
+ if (m_dispatchErrors == 2)
+ {
+ // 2nd time, an error / avoid multiple messages
+ // idea: if it happens once ignore
+ const QString msg =
+ QStringLiteral(u"%1: Dispatch error, sim.status: %2")
+ .arg(this->getSimulatorPluginInfo().getIdentifier(), ISimulator::statusToString(simStatus));
+ CLogMessage(this).log(
+ disconnectedOrNotSimulating ? CStatusMessage::SeverityWarning : CStatusMessage::SeverityError, msg);
+ }
+ else if (m_dispatchErrors > 5)
+ {
+ // this normally happens during a FSX crash or shutdown with simconnect
+ const QString msg =
+ QStringLiteral(u"%1: Multiple dispatch errors, disconnecting. Sim.status: %2")
+ .arg(this->getSimulatorPluginInfo().getIdentifier(), ISimulator::statusToString(simStatus));
+ CLogMessage(this).log(
+ disconnectedOrNotSimulating ? CStatusMessage::SeverityWarning : CStatusMessage::SeverityError, msg);
+ this->disconnectFrom();
+ }
+ return;
+ }
+ m_dispatchErrors = 0;
+ }
+
+ bool CSimulatorMsfs2024::physicallyAddRemoteAircraftImpl(const CSimulatedAircraft &newRemoteAircraft,
+ CSimulatorMsfs2024::AircraftAddMode addMode,
+ const CSimConnectObject &correspondingSimObject)
+ {
+ const CCallsign callsign(newRemoteAircraft.getCallsign());
+ const bool probe = newRemoteAircraft.isTerrainProbe();
+
+ // entry checks
+ Q_ASSERT_X(CThreadUtils::isInThisThread(this), Q_FUNC_INFO, "thread");
+ Q_ASSERT_X(!callsign.isEmpty(), Q_FUNC_INFO, "empty callsign");
+ Q_ASSERT_X(newRemoteAircraft.hasModelString(), Q_FUNC_INFO, "missing model string");
+
+ // reset timer
+ m_simObjectTimer.start(AddPendingAircraftIntervalMs); // restart
+
+ // remove outdated objects
+ const CSimConnectObjects outdatedAdded =
+ m_simConnectObjects.removeOutdatedPendingAdded(CSimConnectObject::AllTypes);
+ if (!outdatedAdded.isEmpty())
+ {
+ const CCallsignSet callsigns = outdatedAdded.getAllCallsigns(false);
+ CLogMessage(this).warning(u"Removed %1 outdated object(s) pending for added: %2")
+ << outdatedAdded.size() << callsigns.getCallsignsAsString(true);
+ this->updateMultipleAircraftEnabled(callsigns, false);
+
+ static const QString msgText("%1 outdated adding, %2");
+ for (const CSimConnectObject &simObjOutdated : outdatedAdded)
+ {
+ const CStatusMessage msg = CStatusMessage(this).warning(
+ msgText.arg(simObjOutdated.getCallsign().asString(), simObjOutdated.toQString()));
+ emit this->physicallyAddingRemoteModelFailed(simObjOutdated.getAircraft(), true, true,
+ msg); // outdated
+ }
+
+ // if this aircraft is also outdated, ignore
+ if (callsigns.contains(newRemoteAircraft.getCallsign())) { return false; }
+ }
+
+ const bool hasPendingAdded = m_simConnectObjects.containsPendingAdded();
+ bool canAdd = this->isSimulating() && !hasPendingAdded;
+
+ Q_ASSERT_X(!hasPendingAdded || m_simConnectObjects.countPendingAdded() < 2, Q_FUNC_INFO,
+ "There must be only 0..1 pending objects");
+ if (this->showDebugLogMessage())
+ {
+ this->debugLogMessage(Q_FUNC_INFO, QStringLiteral("CS: '%1' mode: '%2' model: '%3'")
+ .arg(newRemoteAircraft.getCallsignAsString(), modeToString(addMode),
+ newRemoteAircraft.getModelString()));
+ this->debugLogMessage(
+ Q_FUNC_INFO, QStringLiteral("CS: '%1' pending callsigns: '%2', pending objects: '%3'")
+ .arg(newRemoteAircraft.getCallsignAsString(),
+ m_addPendingAircraft.getAllCallsignStrings(true).join(", "),
+ m_simConnectObjects.getPendingAddedCallsigns().getCallsignStrings().join(", ")));
+ }
+
+ // do we need to remove/add again because something has changed?
+ // this handles changed model strings or an update of the model
+ if (m_simConnectObjects.contains(callsign))
+ {
+ const CSimConnectObject simObject = m_simConnectObjects[callsign];
+ const QString newModelString(newRemoteAircraft.getModelString());
+ const QString simObjModelString(simObject.getAircraftModelString());
+ const bool sameModel =
+ (simObjModelString ==
+ newModelString); // compare on string only (other attributes might change such as mode)
+
+ // same model, nothing will change, otherwise add again when removed
+ if (sameModel)
+ {
+ CLogMessage(this).info(u"CS: '%1' re-added same model '%2'")
+ << newRemoteAircraft.getCallsignAsString() << newModelString;
+
+ // we restore rendered flag in case we are sure we are rendered
+ // this is used with rematching
+ const bool rendered = simObject.isConfirmedAdded() && simObject.isPending();
+ if (rendered) { this->updateAircraftRendered(callsign, rendered); }
+ return true;
+ }
+
+ this->physicallyRemoveRemoteAircraft(newRemoteAircraft.getCallsign());
+ m_addAgainAircraftWhenRemoved.replaceOrAddByCallsign(newRemoteAircraft);
+ if (this->showDebugLogMessage())
+ {
+ this->debugLogMessage(Q_FUNC_INFO,
+ QStringLiteral("CS: '%1' re-added changed model '%2', will be added again")
+ .arg(newRemoteAircraft.getCallsignAsString(), newModelString));
+ }
+ return false;
+ }
+
+ // situation check
+ CAircraftSituation situation(newRemoteAircraft.getSituation());
+ if (canAdd && situation.isPositionOrAltitudeNull())
+ {
+ // invalid position because position or altitude is null
+ const CAircraftSituationList situations(this->remoteAircraftSituations(callsign));
+ if (situations.isEmpty())
+ {
+ CLogMessage(this).warning(u"No valid situations for '%1', will be added as pending")
+ << callsign.asString();
+ }
+ else
+ {
+ CLogMessage(this).warning(u"Invalid aircraft situation for new aircraft '%1', use closest situation")
+ << callsign.asString();
+ situation = situations.findClosestTimeDistanceAdjusted(QDateTime::currentMSecsSinceEpoch());
+ Q_ASSERT_X(!situation.isPositionOrAltitudeNull(), Q_FUNC_INFO, "Invalid situation for new aircraft");
+ }
+
+ // still invalid?
+ canAdd = situation.isPositionOrAltitudeNull();
+ if (CBuildConfig::isLocalDeveloperDebugBuild())
+ {
+ SWIFT_VERIFY_X(canAdd, Q_FUNC_INFO, "Expect valid situation");
+ CLogMessage(this).warning(u"Invalid situation for '%1'") << callsign;
+ }
+ }
+
+ // check if we can add, do not add if simulator is stopped or other objects pending
+ if (!canAdd)
+ {
+ CSimConnectObject &addPendingObj = m_addPendingAircraft[newRemoteAircraft.getCallsign()];
+ addPendingObj.setAircraft(newRemoteAircraft);
+ addPendingObj.resetTimestampToNow();
+ return false;
+ }
+
+ // remove from pending and keep for later to remember fail counters
+ const CSimConnectObject removedPendingObj = this->removeFromAddPendingAndAddAgainAircraft(callsign);
+
+ // create AI after crosschecking it
+ if (!probe && !this->isAircraftInRangeOrTestMode(callsign))
+ {
+ CLogMessage(this).info(u"Skipping adding of '%1' since it is no longer in range") << callsign.asString();
+ return false;
+ }
+
+ // setup
+ const CInterpolationAndRenderingSetupPerCallsign setup =
+ this->getInterpolationSetupConsolidated(callsign, true);
+ const bool sendGround = setup.isSendingGndFlagToSimulator();
+
+ // FSX/P3D adding
+ bool adding = false; // will be added flag
+ const SIMCONNECT_DATA_REQUEST_ID requestId =
+ probe ? this->obtainRequestIdForSimObjTerrainProbe() : this->obtainRequestIdForSimObjAircraft();
+
+ // Initial situation, if possible from interpolation
+ CAircraftSituation initialSituation = newRemoteAircraft.getSituation(); // default
+ {
+ // Dummy CSimConnectObject just for interpolation
+ const CSimConnectObject dummyObject = CSimConnectObject(
+ newRemoteAircraft, 0, this, this, this->getRemoteAircraftProvider(), &m_interpolationLogger);
+ const CInterpolationResult result =
+ dummyObject.getInterpolation(QDateTime::currentMSecsSinceEpoch(), setup, 0);
+ if (result.getInterpolationStatus().isInterpolated())
+ {
+ initialSituation = result.getInterpolatedSituation();
+ }
+ }
+
+ // TODO TZ handle underflow properly
+ CStatusMessage underflowStatus;
+ const SIMCONNECT_DATA_INITPOSITION initialPosition =
+ CSimulatorMsfs2024::aircraftSituationToFsxPosition(initialSituation, sendGround, true, &underflowStatus);
+
+ QString modelString(newRemoteAircraft.getShortModelString());
+ const QString modelLiveryString(newRemoteAircraft.getLiveryString());
+
+ if (this->showDebugLogMessage())
+ {
+ this->debugLogMessage(Q_FUNC_INFO,
+ QStringLiteral("CS: '%1' model: '%2' livery: %3 request: %4, init pos: %5")
+ .arg(callsign.toQString(), modelString, modelLiveryString)
+ .arg(requestId)
+ .arg(fsxPositionToString(initialPosition)));
+ }
+
+ const QByteArray modelStringBa = toFsxChar(modelString);
+ const QByteArray modelLiveryBa = toFsxChar(modelLiveryString);
+
+ const QByteArray csBa = toFsxChar(callsign.toQString().left(12));
+ CSimConnectObject::SimObjectType type = CSimConnectObject::AircraftNonAtc;
+ HRESULT hr = S_OK;
+
+ if (probe)
+ {
+ hr = SimConnect_AICreateNonATCAircraft_EX1(m_hSimConnect, modelStringBa.constData(),
+ modelLiveryBa.constData(), csBa.constData(), initialPosition,
+ requestId);
+
+ type = CSimConnectObject::TerrainProbe;
+ }
+ else
+ {
+ if (this->isAddingAsSimulatedObjectEnabled() && correspondingSimObject.hasCallsign() &&
+ correspondingSimObject.getAddingExceptions() > 0 &&
+ correspondingSimObject.getType() == CSimConnectObject::AircraftNonAtc)
+ {
+ CStatusMessage(this).warning(
+ u"Model '%1' for '%2' failed %1 time(s) before, using AICreateSimulatedObject now")
+ << newRemoteAircraft.getModelString() << callsign.toQString();
+
+ hr = SimConnect_AICreateNonATCAircraft_EX1(m_hSimConnect, modelStringBa.constData(),
+ modelLiveryBa.constData(), csBa.constData(), initialPosition,
+ requestId);
+
+ type = CSimConnectObject::AircraftSimulatedObject;
+ }
+ else
+ {
+ hr = SimConnect_AICreateNonATCAircraft_EX1(m_hSimConnect, modelStringBa.constData(),
+ modelLiveryBa.constData(), csBa.constData(), initialPosition,
+ requestId);
+
+ type = CSimConnectObject::AircraftNonAtc;
+ }
+ }
+
+ if (!underflowStatus.isEmpty())
+ {
+ CStatusMessage(this).warning(u"Underflow detecion for '%1', details '%2'")
+ << callsign.asString() << underflowStatus.getMessage();
+ }
+
+ if (isFailure(hr))
+ {
+ const CStatusMessage msg = CStatusMessage(this).error(u"SimConnect, can not create AI traffic: '%1' '%2'")
+ << callsign.toQString() << modelString;
+ CLogMessage::preformatted(msg);
+ emit this->physicallyAddingRemoteModelFailed(newRemoteAircraft, true, true, msg); // SimConnect error
+ }
+ else
+ {
+ // we will request a new aircraft by request ID, later we will receive its object id
+ // so far this object id is 0 (DWORD)
+ const CSimConnectObject simObject =
+ this->insertNewSimConnectObject(newRemoteAircraft, requestId, type, removedPendingObj);
+ this->traceSendId(simObject, Q_FUNC_INFO,
+ QStringLiteral("mode: %1").arg(CSimulatorMsfs2024::modeToString(addMode)), true);
+ adding = true;
+ }
+ return adding;
+ }
+
+ bool CSimulatorMsfs2024::physicallyAddAITerrainProbe(const ICoordinateGeodetic &coordinate, int number)
+ {
+ if (coordinate.isNull()) { return false; }
+ if (!this->isUsingFsxTerrainProbe()) { return false; }
+ Q_ASSERT_X(CThreadUtils::isInThisThread(this), Q_FUNC_INFO, "thread");
+
+ // static const QString modelString("OrcaWhale");
+ // static const QString modelString("Water Drop"); // not working on P3Dx86/FSX, no requests on that id
+ // possible static const QString modelString("A321ACA"); static const QString
+ // modelString("AI_Tracker_Object_0"); static const QString modelString("Piper Cub"); // P3Dv86 works as
+ // nonATC/SimulatedObject static const QString modelString("Discovery Spaceshuttle"); // P3Dx86 works as
+ // nonATC/SimulatedObject
+ static const QString modelString("swiftTerrainProbe0");
+ static const QString pseudoCallsign("PROBE%1"); // max 12 chars
+ static const CCountry ctry("SW", "SWIFT");
+ static const CAirlineIcaoCode swiftAirline("SWI", "swift probe", ctry, "SWIFT", false, false);
+ static const CLivery swiftLivery(CLivery::getStandardCode(swiftAirline), swiftAirline, "swift probe");
+
+ const CCallsign cs(pseudoCallsign.arg(number));
+ const CAircraftModel model(modelString, CAircraftModel::TypeTerrainProbe, QStringLiteral("swift terrain probe"),
+ CAircraftIcaoCode::unassignedIcao(), swiftLivery);
+ CAircraftSituation situation(cs, coordinate);
+ situation.setAltitude(terrainProbeAltitude());
+ situation.setZeroPBH();
+ const CSimulatedAircraft pseudoAircraft(cs, model, CUser("123456", "swift", cs), situation);
+ return this->physicallyAddRemoteAircraftImpl(pseudoAircraft, ExternalCall);
+ }
+
+ int CSimulatorMsfs2024::physicallyInitAITerrainProbes(const ICoordinateGeodetic &coordinate, int number)
+ {
+ if (number < 1) { return 0; }
+ if (m_initFsxTerrainProbes) { return m_addedProbes; }
+ m_initFsxTerrainProbes = true; // no multiple inits
+ this->triggerAutoTraceSendId();
+
+ int c = 0;
+ for (int n = 0; n < number; ++n)
+ {
+ if (this->physicallyAddAITerrainProbe(coordinate, n)) { c++; }
+ }
+
+ CLogMessage(this).info(u"Adding %1 FSX terrain probes") << number;
+ m_addedProbes = c;
+ return c;
+ }
+
+ bool CSimulatorMsfs2024::physicallyRemoveRemoteAircraft(const CCallsign &callsign)
+ {
+ // only remove from sim
+ Q_ASSERT_X(CThreadUtils::isInThisThread(this), Q_FUNC_INFO, "wrong thread");
+ if (callsign.isEmpty()) { return false; } // can happen if an object is not an aircraft
+
+ // clean up anyway
+ this->removeFromAddPendingAndAddAgainAircraft(callsign);
+
+ // really remove from simulator
+ if (!m_simConnectObjects.contains(callsign)) { return false; } // already fully removed or not yet added
+ CSimConnectObject &simObject = m_simConnectObjects[callsign];
+ if (simObject.isPendingRemoved()) { return true; }
+ if (simObject.isTerrainProbe()) { return false; }
+
+ // check for pending objects
+ m_addPendingAircraft.remove(callsign); // just in case still in list of pending aircraft
+ const bool pendingAdded = simObject.isPendingAdded(); // already added in simulator, but not yet confirmed
+ const bool stillWaitingForLights = !simObject.hasCurrentLightsInSimulator();
+ if (!simObject.isRemovedWhileAdding() && (pendingAdded || stillWaitingForLights))
+ {
+ // problem: we try to delete an aircraft just requested to be added
+ // best solution so far, call remove again with a delay
+ CLogMessage(this).warning(u"'%1' requested to be removed, but pending added (%2) / or pending lights(%3). "
+ u"Object will be removed again: %4")
+ << callsign.asString() << boolToYesNo(pendingAdded) << boolToYesNo(stillWaitingForLights)
+ << simObject.toQString();
+ simObject.setRemovedWhileAdding(true); // next time kill
+ QPointer myself(this);
+ QTimer::singleShot(2000, this, [=] {
+ if (!myself) { return; }
+ CLogMessage(this).info(u"Next trial to remove '%1'") << callsign.asString();
+ myself->physicallyRemoveRemoteAircraft(callsign);
+ });
+ return false; // not yet deleted
+ }
+
+ // no more data from simulator
+ this->stopRequestingDataForSimObject(simObject);
+
+ // mark as removed
+ simObject.setPendingRemoved(true);
+ if (this->showDebugLogMessage())
+ {
+ this->debugLogMessage(Q_FUNC_INFO, QStringLiteral("CS: '%1' request/object id: %2/%3")
+ .arg(callsign.toQString())
+ .arg(simObject.getRequestId())
+ .arg(simObject.getObjectId()));
+ }
+
+ // call in SIM
+ const SIMCONNECT_DATA_REQUEST_ID requestId = simObject.getRequestId(CSimConnectDefinitions::SimObjectRemove);
+ this->removeCamera(simObject);
+ this->removeObserver(simObject);
+ const HRESULT result = SimConnect_AIRemoveObject(
+ m_hSimConnect, static_cast(simObject.getObjectId()), requestId);
+ if (isOk(result))
+ {
+ if (this->isTracingSendId()) { this->traceSendId(simObject, Q_FUNC_INFO); }
+ }
+ else { CLogMessage(this).warning(u"Removing aircraft '%1' from simulator failed") << callsign.asString(); }
+
+ // mark in provider
+ const bool updated = this->updateAircraftRendered(callsign, false);
+ if (updated)
+ {
+ CSimulatedAircraft aircraft(simObject.getAircraft());
+ aircraft.setRendered(false);
+ emit this->aircraftRenderingChanged(aircraft);
+ }
+
+ // cleanup function, actually this should not be needed
+ this->physicallyRemoveAircraftNotInProviderAsync();
+
+ // bye
+ return CSimulatorPluginCommon::physicallyRemoveRemoteAircraft(callsign);
+ }
+
+ int CSimulatorMsfs2024::physicallyRemoveAllRemoteAircraft()
+ {
+ // make sure they are not added again
+ // cleaning here is somewhat redundant, but double checks
+ m_addPendingAircraft.clear();
+ m_addAgainAircraftWhenRemoved.clear();
+
+ // remove one by one
+ int r = 0;
+ const CCallsignSet callsigns = m_simConnectObjects.getAllCallsigns();
+ for (const CCallsign &cs : callsigns)
+ {
+ if (this->physicallyRemoveRemoteAircraft(cs)) { r++; }
+ }
+
+ CSimulatorFsCommon::physicallyRemoveAllRemoteAircraft();
+ return r;
+ }
+
+ HRESULT CSimulatorMsfs2024::initEvents()
+ {
+ HRESULT hr = s_ok();
+ // System events, see
+ // http://msdn.microsoft.com/en-us/library/cc526983.aspx#SimConnect_SubscribeToSystemEvent
+ hr += SimConnect_SubscribeToSystemEvent(m_hSimConnect, SystemEventSimStatus, "Sim");
+ hr += SimConnect_SubscribeToSystemEvent(m_hSimConnect, SystemEventObjectAdded, "ObjectAdded");
+ hr += SimConnect_SubscribeToSystemEvent(m_hSimConnect, SystemEventObjectRemoved, "ObjectRemoved");
+ hr += SimConnect_SubscribeToSystemEvent(m_hSimConnect, SystemEventFrame, "Frame");
+ hr += SimConnect_SubscribeToSystemEvent(m_hSimConnect, SystemEventPause, "Pause");
+ hr += SimConnect_SubscribeToSystemEvent(m_hSimConnect, SystemEventFlightLoaded, "FlightLoaded");
+ if (isFailure(hr))
+ {
+ CLogMessage(this).error(u"FSX plugin error: %1") << "SimConnect_SubscribeToSystemEvent failed";
+ return hr;
+ }
+
+ // Mapped events, see event ids here:
+ // http://msdn.microsoft.com/en-us/library/cc526980.aspx
+ // http://www.prepar3d.com/SDKv2/LearningCenter/utilities/variables/event_ids.html
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventPauseToggle, "PAUSE_TOGGLE");
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, SystemEventSlewToggle, "SLEW_TOGGLE");
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventFreezeLatLng,
+ "FREEZE_LATITUDE_LONGITUDE_SET"); // FSX old standard
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventFreezeAlt,
+ "FREEZE_ALTITUDE_SET"); // FSX old standard
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventFreezeAtt,
+ "FREEZE_ATTITUDE_SET"); // FSX old standard
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventSetCom1Active, "COM_RADIO_SET");
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventSetCom1Standby, "COM_STBY_RADIO_SET");
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventSetCom2Active, "COM2_RADIO_SET");
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventSetCom2Standby, "COM2_STBY_RADIO_SET");
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventSetTransponderCode, "XPNDR_SET");
+
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventSetTimeZuluYear, "ZULU_YEAR_SET");
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventSetTimeZuluDay, "ZULU_DAY_SET");
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventSetTimeZuluHours, "ZULU_HOURS_SET");
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventSetTimeZuluMinutes, "ZULU_MINUTES_SET");
+
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventLandingLightsOff, "LANDING_LIGHTS_OFF");
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventLandinglightsOn, "LANDING_LIGHTS_ON");
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventLandingLightsSet, "LANDING_LIGHTS_SET");
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventLandingLightsToggle, "LANDING_LIGHTS_TOGGLE");
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventPanelLightsOff, "PANEL_LIGHTS_OFF");
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventPanelLightsOn, "PANEL_LIGHTS_ON");
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventPanelLightsSet, "PANEL_LIGHTS_SET");
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventStrobesOff, "STROBES_OFF");
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventStrobesOn, "STROBES_ON");
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventStrobesSet, "STROBES_SET");
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventStrobesToggle, "STROBES_TOGGLE");
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventToggleBeaconLights, "TOGGLE_BEACON_LIGHTS");
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventToggleCabinLights, "TOGGLE_CABIN_LIGHTS");
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventToggleLogoLights, "TOGGLE_LOGO_LIGHTS");
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventToggleNavLights, "TOGGLE_NAV_LIGHTS");
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventToggleRecognitionLights,
+ "TOGGLE_RECOGNITION_LIGHTS");
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventToggleTaxiLights, "TOGGLE_TAXI_LIGHTS");
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventToggleWingLights, "TOGGLE_WING_LIGHTS");
+
+ hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventFlapsSet, "FLAPS_SET");
+
+ if (isFailure(hr))
+ {
+ CLogMessage(this).error(u"FSX plugin error: %1") << "SimConnect_MapClientEventToSimEvent failed";
+ return hr;
+ }
+
+ // facility
+ SIMCONNECT_DATA_REQUEST_ID requestId =
+ static_cast(CSimConnectDefinitions::RequestFacility);
+ hr += SimConnect_SubscribeToFacilities(m_hSimConnect, SIMCONNECT_FACILITY_LIST_TYPE_AIRPORT, requestId);
+ if (isFailure(hr))
+ {
+ CLogMessage(this).error(u"FSX plugin error: %1") << "SimConnect_SubscribeToFacilities failed";
+ return hr;
+ }
+ return hr;
+ }
+
+ HRESULT CSimulatorMsfs2024::initDataDefinitionsWhenConnected()
+ {
+
+ return CSimConnectDefinitions::initDataDefinitionsWhenConnected(
+ m_hSimConnect, this->getSimulatorPluginInfo().getSimulatorInfo());
+ }
+
+ HRESULT CSimulatorMsfs2024::initWhenConnected()
+ {
+ // called when connected
+
+ HRESULT hr = this->initEvents();
+ if (isFailure(hr))
+ {
+ CLogMessage(this).error(u"FSX plugin: initEvents failed");
+ return hr;
+ }
+
+ // init data definitions and SB data area
+ hr += this->initDataDefinitionsWhenConnected();
+ if (isFailure(hr))
+ {
+ CLogMessage(this).error(u"FSX plugin: initDataDefinitionsWhenConnected failed");
+ return hr;
+ }
+
+ return hr;
+ }
+
+ void CSimulatorMsfs2024::updateRemoteAircraft()
+ {
+ static_assert(sizeof(DataDefinitionRemoteAircraftPartsWithoutLights) == sizeof(double) * 10,
+ "DataDefinitionRemoteAircraftPartsWithoutLights has an incorrect size.");
+ Q_ASSERT_X(CThreadUtils::isInThisThread(this), Q_FUNC_INFO, "thread");
+
+ // nothing to do, reset request id and exit
+ const int remoteAircraftNo = this->getAircraftInRangeCount();
+ if (remoteAircraftNo < 1)
+ {
+ m_statsUpdateAircraftRuns = 0;
+ return;
+ }
+
+ // values used for position and parts
+ const qint64 currentTimestamp = QDateTime::currentMSecsSinceEpoch();
+ if (this->isUpdateAircraftLimitedWithStats(currentTimestamp))
+ {
+ this->finishUpdateRemoteAircraftAndSetStatistics(currentTimestamp, true);
+ return;
+ }
+ m_updateRemoteAircraftInProgress = true;
+
+ // interpolation for all remote aircraft
+ const QList simObjects(m_simConnectObjects.values());
+
+ uint32_t simObjectNumber = 0;
+ const bool traceSendId = this->isTracingSendId();
+ const bool updateAllAircraft = this->isUpdateAllRemoteAircraft(currentTimestamp);
+ for (const CSimConnectObject &simObject : simObjects)
+ {
+ // happening if aircraft is not yet added to simulator or to be deleted
+ if (!simObject.isReadyToSend()) { continue; }
+ if (!simObject.hasCurrentLightsInSimulator()) { continue; } // wait until we have light state
+
+ const CCallsign callsign(simObject.getCallsign());
+ const bool hasCs = !callsign.isEmpty();
+ const bool hasValidIds = simObject.hasValidRequestAndObjectId();
+ SWIFT_VERIFY_X(hasCs, Q_FUNC_INFO, "missing callsign");
+ SWIFT_AUDIT_X(hasValidIds, Q_FUNC_INFO, "Missing ids");
+ if (!hasCs || !hasValidIds) { continue; } // not supposed to happen
+ const DWORD objectId = simObject.getObjectId();
+
+ // setup
+ const CInterpolationAndRenderingSetupPerCallsign setup =
+ this->getInterpolationSetupConsolidated(callsign, updateAllAircraft);
+ const bool sendGround = setup.isSendingGndFlagToSimulator();
+
+ // Interpolated situation
+ // simObjectNumber is passed to equally distributed steps like guessing parts
+ const bool slowUpdate = (((m_statsUpdateAircraftRuns + simObjectNumber) % 40) == 0);
+ const CInterpolationResult result = simObject.getInterpolation(currentTimestamp, setup, simObjectNumber++);
+ const bool forceUpdate = slowUpdate || updateAllAircraft || setup.isForcingFullInterpolation();
+ if (result.getInterpolationStatus().hasValidSituation())
+ {
+ // update situation
+ if (forceUpdate || !this->isEqualLastSent(result.getInterpolatedSituation()))
+ {
+ // adjust altitude to compensate for FS2020 temperature effect
+ CAircraftSituation situation = result;
+ const CLength relativeAltitude =
+ situation.geodeticHeight() - getOwnAircraftPosition().geodeticHeight();
+ const double altitudeDeltaWeight =
+ 2 - qBound(3000.0, relativeAltitude.abs().value(CLengthUnit::ft()), 6000.0) / 3000;
+ situation.setAltitude({ situation.getAltitude() + m_altitudeDelta * altitudeDeltaWeight,
+ situation.getAltitude().getReferenceDatum() });
+
+ SIMCONNECT_DATA_INITPOSITION position = this->aircraftSituationToFsxPosition(situation, sendGround);
+ const HRESULT hr = this->logAndTraceSendId(
+ SimConnect_SetDataOnSimObject(m_hSimConnect,
+ CSimConnectDefinitions::DataRemoteAircraftSetPosition,
+ static_cast(objectId), 0, 0,
+ sizeof(SIMCONNECT_DATA_INITPOSITION), &position),
+ traceSendId, simObject, "Failed to set position", Q_FUNC_INFO, "SimConnect_SetDataOnSimObject");
+ if (isOk(hr))
+ {
+ this->rememberLastSent(result); // remember situation
+ }
+ }
+ }
+ else
+ {
+ // already logged in interpolator
+ continue;
+ }
+
+ // Interpolated parts
+ const bool updatedParts = this->updateRemoteAircraftParts(simObject, result, forceUpdate);
+ Q_UNUSED(updatedParts)
+
+ } // all callsigns
+
+ // stats
+ this->finishUpdateRemoteAircraftAndSetStatistics(currentTimestamp);
+ }
+
+ bool CSimulatorMsfs2024::updateRemoteAircraftParts(const CSimConnectObject &simObject,
+ const CInterpolationResult &result, bool forcedUpdate)
+ {
+ if (!simObject.hasValidRequestAndObjectId()) { return false; }
+ if (!simObject.isConfirmedAdded()) { return false; }
+
+ const CAircraftParts parts = result;
+ if (parts.isNull()) { return false; }
+ if (parts.getPartsDetails() != CAircraftParts::GuessedParts && !result.getPartsStatus().isSupportingParts())
+ {
+ return false;
+ }
+
+ const CCallsign cs = simObject.getCallsign();
+ if (!forcedUpdate && (result.getPartsStatus().isReusedParts() || this->isEqualLastSent(parts, cs)))
+ {
+ return true;
+ }
+
+ const bool ok = this->sendRemoteAircraftPartsToSimulator(simObject, parts);
+ if (ok) { this->rememberLastSent(parts, cs); }
+ return ok;
+ }
+
+ // TODO TZ under investigation, flaps retracting to 0 again and other issues
+ bool CSimulatorMsfs2024::sendRemoteAircraftPartsToSimulator(const CSimConnectObject &simObject,
+ const CAircraftParts &parts)
+ {
+ Q_ASSERT(m_hSimConnect);
+ if (!simObject.isReadyToSend()) { return false; }
+
+ const DWORD objectId = simObject.getObjectId();
+ const bool traceId = this->isTracingSendId();
+
+ DataDefinitionRemoteAircraftPartsWithoutLights ddRemoteAircraftPartsWithoutLights(parts);
+ const CAircraftLights lights = parts.getAdjustedLights();
+
+ // in case we sent, we sent everything
+ const bool simObjectAircraftType = simObject.isAircraftSimulatedObject(); // no real aircraft type
+ const HRESULT hr1 =
+ simObjectAircraftType ?
+ S_OK :
+ this->logAndTraceSendId(
+ SimConnect_SetDataOnSimObject(
+ m_hSimConnect, CSimConnectDefinitions::DataRemoteAircraftPartsWithoutLights,
+ static_cast(objectId), SIMCONNECT_DATA_SET_FLAG_DEFAULT, 0,
+ sizeof(DataDefinitionRemoteAircraftPartsWithoutLights), &ddRemoteAircraftPartsWithoutLights),
+ traceId, simObject, "Failed so set parts", Q_FUNC_INFO,
+ "SimConnect_SetDataOnSimObject::ddRemoteAircraftPartsWithoutLights");
+
+ // Sim variable version, not working, setting the value, but flaps retracting to 0 again
+ // Sets flap handle to closest increment (0 to 16383)
+ const DWORD flapsDw = static_cast(qMin(16383, qRound((parts.getFlapsPercent() / 100.0) * 16383)));
+ const HRESULT hr2 = this->logAndTraceSendId(
+ SimConnect_TransmitClientEvent(m_hSimConnect, objectId, EventFlapsSet, flapsDw,
+ SIMCONNECT_GROUP_PRIORITY_HIGHEST,
+ SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY),
+ traceId, simObject, "Failed so set flaps", Q_FUNC_INFO, "SimConnect_TransmitClientEvent::EventFlapsSet");
+
+ // lights we can set directly
+ const HRESULT hr3 = this->logAndTraceSendId(
+ SimConnect_TransmitClientEvent(m_hSimConnect, objectId, EventLandingLightsSet,
+ lights.isLandingOn() ? 1.0 : 0.0, SIMCONNECT_GROUP_PRIORITY_HIGHEST,
+ SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY),
+ traceId, simObject, "Failed so set landing lights", Q_FUNC_INFO,
+ "SimConnect_TransmitClientEvent::EventLandingLightsSet");
+
+ const HRESULT hr4 =
+ this->logAndTraceSendId(SimConnect_TransmitClientEvent(
+ m_hSimConnect, objectId, EventStrobesSet, lights.isStrobeOn() ? 1.0 : 0.0,
+ SIMCONNECT_GROUP_PRIORITY_HIGHEST, SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY),
+ traceId, simObject, "Failed to set strobe lights", Q_FUNC_INFO,
+ "SimConnect_TransmitClientEvent::EventStrobesSet");
+
+ // lights we need to toggle
+ // (potential risk with quickly changing values that we accidentally toggle back, also we need the light
+ // state before we can toggle)
+ this->sendToggledLightsToSimulator(simObject, lights);
+
+ // done
+ return isOk(hr1, hr2, hr3, hr4);
+ }
+
+ bool CSimulatorMsfs2024::sendRemoteAircraftAtcDataToSimulator(const CSimConnectObject &simObject)
+ {
+ if (!simObject.isReadyToSend()) { return false; }
+ if (simObject.isTerrainProbe()) { return false; }
+ // if (simObject.getType() != CSimConnectObject::AircraftNonAtc) { return false; } // otherwise errors
+
+ const DWORD objectId = simObject.getObjectId();
+ const bool traceId = this->isTracingSendId();
+
+ DataDefinitionRemoteAtc ddAtc;
+ ddAtc.setDefaultValues();
+ const QByteArray csBa = simObject.getCallsignByteArray();
+ const QByteArray airlineBa = simObject.getAircraft().getAirlineIcaoCode().getName().toLatin1();
+ const QByteArray flightNumberBa = QString::number(simObject.getObjectId()).toLatin1();
+
+ ddAtc.copyAtcId(csBa.constData());
+ ddAtc.copyAtcAirline(airlineBa.constData());
+ ddAtc.copyFlightNumber(flightNumberBa.constData());
+
+ // in case we sent, we sent everything
+ const HRESULT hr = this->logAndTraceSendId(
+ SimConnect_SetDataOnSimObject(m_hSimConnect, CSimConnectDefinitions::DataRemoteAircraftSetData,
+ static_cast(objectId), SIMCONNECT_DATA_SET_FLAG_DEFAULT,
+ 0, sizeof(DataDefinitionRemoteAtc), &ddAtc),
+ traceId, simObject, "Failed so aircraft ATC data", Q_FUNC_INFO, "SimConnect_SetDataOnSimObject");
+ // done
+ return isOk(hr);
+ }
+
+ void CSimulatorMsfs2024::sendToggledLightsToSimulator(const CSimConnectObject &simObj,
+ const CAircraftLights &lightsWanted, bool force)
+ {
+ if (!simObj.isReadyToSend()) { return; } // stale
+
+ const CAircraftLights lightsIsState = simObj.getCurrentLightsInSimulator();
+ if (lightsWanted == lightsIsState) { return; }
+ if (!force && lightsWanted == simObj.getLightsAsSent()) { return; }
+ const CCallsign callsign(simObj.getCallsign());
+
+ // Update data
+ if (m_simConnectObjects.contains(callsign))
+ {
+ CSimConnectObject &simObjToUpdate = m_simConnectObjects[callsign];
+ simObjToUpdate.setLightsAsSent(lightsWanted);
+ }
+
+ // state available, then I can toggle
+ if (!lightsIsState.isNull())
+ {
+ const DWORD objectId = simObj.getObjectId();
+ const bool trace = this->isTracingSendId();
+
+ if (lightsWanted.isTaxiOn() != lightsIsState.isTaxiOn())
+ {
+ this->logAndTraceSendId(SimConnect_TransmitClientEvent(m_hSimConnect, objectId, EventToggleTaxiLights,
+ 0.0, SIMCONNECT_GROUP_PRIORITY_HIGHEST,
+ SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY),
+ trace, simObj, "Toggle taxi lights", Q_FUNC_INFO, "EventToggleTaxiLights");
+ }
+ if (lightsWanted.isNavOn() != lightsIsState.isNavOn())
+ {
+ this->logAndTraceSendId(SimConnect_TransmitClientEvent(m_hSimConnect, objectId, EventToggleNavLights,
+ 0.0, SIMCONNECT_GROUP_PRIORITY_HIGHEST,
+ SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY),
+ trace, simObj, "Toggle nav.lights", Q_FUNC_INFO, "EventToggleNavLights");
+ }
+ if (lightsWanted.isBeaconOn() != lightsIsState.isBeaconOn())
+ {
+ this->logAndTraceSendId(SimConnect_TransmitClientEvent(m_hSimConnect, objectId, EventToggleBeaconLights,
+ 0.0, SIMCONNECT_GROUP_PRIORITY_HIGHEST,
+ SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY),
+ trace, simObj, "Toggle becon lights", Q_FUNC_INFO, "EventToggleBeaconLights");
+ }
+ if (lightsWanted.isLogoOn() != lightsIsState.isLogoOn())
+ {
+ this->logAndTraceSendId(SimConnect_TransmitClientEvent(m_hSimConnect, objectId, EventToggleLogoLights,
+ 0.0, SIMCONNECT_GROUP_PRIORITY_HIGHEST,
+ SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY),
+ trace, simObj, "Toggle logo lights", Q_FUNC_INFO, "EventToggleLogoLights");
+ }
+ if (lightsWanted.isRecognitionOn() != lightsIsState.isRecognitionOn())
+ {
+ this->logAndTraceSendId(
+ SimConnect_TransmitClientEvent(m_hSimConnect, objectId, EventToggleRecognitionLights, 0.0,
+ SIMCONNECT_GROUP_PRIORITY_HIGHEST,
+ SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY),
+ trace, simObj, "Toggle recognition lights", Q_FUNC_INFO, "EventToggleRecognitionLights");
+ }
+ if (lightsWanted.isCabinOn() != lightsIsState.isCabinOn())
+ {
+ this->logAndTraceSendId(SimConnect_TransmitClientEvent(m_hSimConnect, objectId, EventToggleCabinLights,
+ 0.0, SIMCONNECT_GROUP_PRIORITY_HIGHEST,
+ SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY),
+ trace, simObj, "Toggle cabin lights", Q_FUNC_INFO, "EventToggleCabinLights");
+ }
+ return;
+ }
+
+ // missing lights info from simulator so far
+ if (this->showDebugLogMessage())
+ {
+ this->debugLogMessage(Q_FUNC_INFO, QStringLiteral("Missing light state in simulator for '%1', model '%2'")
+ .arg(callsign.asString(), simObj.getAircraftModelString()));
+ }
+
+ const QPointer myself(this);
+ QTimer::singleShot(DeferResendingLights, this, [=] {
+ if (!myself) { return; }
+ if (!m_simConnectObjects.contains(callsign)) { return; }
+ const CSimConnectObject currentSimObject = m_simConnectObjects[callsign];
+ if (!currentSimObject.isReadyToSend()) { return; } // stale
+ if (lightsWanted != currentSimObject.getLightsAsSent())
+ {
+ return;
+ } // changed in between, so another call sendToggledLightsToSimulator is pending
+ if (this->showDebugLogMessage())
+ {
+ this->debugLogMessage(Q_FUNC_INFO, QStringLiteral("Resending light state for '%1', model '%2'")
+ .arg(callsign.asString(), simObj.getAircraftModelString()));
+ }
+ this->sendToggledLightsToSimulator(currentSimObject, lightsWanted, true);
+ });
+ }
+
+ SIMCONNECT_DATA_INITPOSITION
+ CSimulatorMsfs2024::aircraftSituationToFsxPosition(const CAircraftSituation &situation, bool sendGnd,
+ bool forceUnderflowDetection, CStatusMessage *details)
+ {
+ Q_ASSERT_X(!situation.isGeodeticHeightNull(), Q_FUNC_INFO, "Missing height (altitude)");
+ Q_ASSERT_X(!situation.isPositionNull(), Q_FUNC_INFO, "Missing position");
+
+ // lat/Lng, NO PBH
+ CAircraftSituation::AltitudeCorrection altCorrection = CAircraftSituation::UnknownCorrection;
+ SIMCONNECT_DATA_INITPOSITION position = CSimulatorMsfs2024::coordinateToFsxPosition(situation);
+ if (forceUnderflowDetection)
+ {
+ const CAltitude alt = situation.getCorrectedAltitude(true, &altCorrection);
+ position.Altitude = alt.value(CLengthUnit::ft());
+ }
+
+ // MSFS has inverted pitch and bank angles
+ position.Pitch = -situation.getPitch().value(CAngleUnit::deg());
+ position.Bank = -situation.getBank().value(CAngleUnit::deg());
+ position.Heading = situation.getHeading().value(CAngleUnit::deg());
+ position.OnGround = 0U; // not on ground
+
+ const double gsKts = situation.getGroundSpeed().value(CSpeedUnit::kts());
+ position.Airspeed = static_cast(qRound(gsKts));
+
+ // sanity check
+ if (gsKts < 0.0)
+ {
+ // we get negative GS for pushback and helicopters
+ // here we handle them her with DWORD (unsigned)
+ position.Airspeed = 0U;
+ }
+ else { position.Airspeed = static_cast(qRound(gsKts)); }
+
+ // send GND flag also when underflow detection is available
+ if ((sendGnd || forceUnderflowDetection) && situation.isOnGroundInfoAvailable())
+ {
+ const bool onGround = situation.isOnGround();
+ position.OnGround = onGround ? 1U : 0U;
+ }
+
+ // if we have no GND flag yet (gnd flag prevents underflow)
+ if (forceUnderflowDetection && position.OnGround == 0 &&
+ !CAircraftSituation::isCorrectedAltitude(altCorrection))
+ {
+ // logical resolution failed so far, likely we have no CG or elevantion
+ // primitive guessing
+ do {
+ if (position.Airspeed < 2)
+ {
+ position.OnGround = 1U;
+ if (details)
+ {
+ *details = CStatusMessage(static_cast(nullptr))
+ .warning(u"Force GND flag for underflow protection");
+ }
+ break;
+ }
+ }
+ while (false);
+ }
+
+ // crosscheck
+ if (CBuildConfig::isLocalDeveloperDebugBuild())
+ {
+ SWIFT_VERIFY_X(isValidFsxPosition(position), Q_FUNC_INFO, "Invalid FSX pos.");
+ }
+
+ return position;
+ }
+
+ SIMCONNECT_DATA_PBH CSimulatorMsfs2024::aircraftSituationToFsxPBH(const CAircraftSituation &situation)
+ {
+ // MSFS has inverted pitch and bank angles
+ SIMCONNECT_DATA_PBH pbh;
+ pbh.Pitch = -situation.getPitch().value(CAngleUnit::deg());
+ pbh.Bank = -situation.getBank().value(CAngleUnit::deg());
+ pbh.Heading = situation.getHeading().value(CAngleUnit::deg());
+ return pbh;
+ }
+
+ SIMCONNECT_DATA_INITPOSITION
+ CSimulatorMsfs2024::coordinateToFsxPosition(const ICoordinateGeodetic &coordinate)
+ {
+ SIMCONNECT_DATA_INITPOSITION position;
+ position.Latitude = coordinate.latitude().value(CAngleUnit::deg());
+ position.Longitude = coordinate.longitude().value(CAngleUnit::deg());
+ position.Altitude = coordinate.geodeticHeight().value(
+ CLengthUnit::ft()); // already corrected in interpolator if there is an underflow
+ position.Heading = 0;
+ position.Airspeed = 0;
+ position.Pitch = 0;
+ position.Bank = 0;
+ position.OnGround = 0;
+ return position;
+ }
+
+ SIMCONNECT_DATA_LATLONALT CSimulatorMsfs2024::coordinateToFsxLatLonAlt(const ICoordinateGeodetic &coordinate)
+ {
+ SIMCONNECT_DATA_LATLONALT lla;
+ lla.Latitude = coordinate.latitude().value(CAngleUnit::deg());
+ lla.Longitude = coordinate.longitude().value(CAngleUnit::deg());
+ lla.Altitude = coordinate.geodeticHeight().value(
+ CLengthUnit::ft()); // already corrected in interpolator if there is an underflow
+ return lla;
+ }
+
+ bool CSimulatorMsfs2024::isValidFsxPosition(const SIMCONNECT_DATA_INITPOSITION &fsxPos)
+ {
+ // double Latitude; // degrees | double Longitude; // degrees | double Altitude; // feet
+ // double Pitch; // degrees | double Bank; // degrees | double Heading; // degrees
+ // DWORD OnGround; // 1=force to be on the ground | DWORD Airspeed; // knots
+ // https://www.prepar3d.com/SDKv4/sdk/simconnect_api/references/simobject_functions.html
+ // examples show heaading 180 => we assume values +-180deg
+ if (!isValid180Deg(fsxPos.Pitch)) { return false; }
+ if (!isValid180Deg(fsxPos.Bank)) { return false; }
+ if (!isValid180Deg(fsxPos.Heading)) { return false; }
+ if (!isValid180Deg(fsxPos.Latitude)) { return false; }
+ if (!isValid180Deg(fsxPos.Longitude)) { return false; }
+ return true;
+ }
+
+ bool CSimulatorMsfs2024::requestPositionDataForSimObject(const CSimConnectObject &simObject,
+ SIMCONNECT_PERIOD period)
+ {
+ if (this->isShuttingDownOrDisconnected()) { return false; }
+ if (!simObject.hasValidRequestAndObjectId()) { return false; }
+ if (simObject.isPending()) { return false; } // wait until confirmed
+ if (simObject.getSimDataPeriod() == period) { return true; } // already queried like this
+ if (!m_simConnectObjects.contains(simObject.getCallsign())) { return false; } // removed in meantime
+
+ // always request, not only when something has changed
+ const SIMCONNECT_DATA_REQUEST_ID reqId = static_cast(
+ simObject.getRequestId(CSimConnectDefinitions::SimObjectPositionData));
+ const HRESULT result = this->logAndTraceSendId(
+ SimConnect_RequestDataOnSimObject(m_hSimConnect, reqId,
+ CSimConnectDefinitions::DataRemoteAircraftGetPosition,
+ simObject.getObjectId(), period),
+ simObject, "Cannot request simulator data", Q_FUNC_INFO, "SimConnect_RequestDataOnSimObject");
+
+ if (isOk(result))
+ {
+ m_requestSimObjectDataCount++;
+ m_simConnectObjects[simObject.getCallsign()].setSimDataPeriod(period);
+ return true;
+ }
+ return false;
+ }
+
+ bool CSimulatorMsfs2024::requestTerrainProbeData(const CSimConnectObject &simObject,
+ const CCallsign &aircraftCallsign)
+ {
+ static const QString w("Cannot request terrain probe data for id '%1'");
+ const SIMCONNECT_DATA_REQUEST_ID requestId =
+ simObject.getRequestId(CSimConnectDefinitions::SimObjectPositionData);
+ const DWORD objectId = simObject.getObjectId();
+ const HRESULT result = this->logAndTraceSendId(
+ SimConnect_RequestDataOnSimObject(m_hSimConnect, static_cast(requestId),
+ CSimConnectDefinitions::DataRemoteAircraftGetPosition,
+ static_cast(objectId), SIMCONNECT_PERIOD_ONCE),
+ simObject, w.arg(requestId), Q_FUNC_INFO, "SimConnect_RequestDataOnSimObject");
+ const bool ok = isOk(result);
+ if (ok) { m_pendingProbeRequests.insert(requestId, aircraftCallsign); }
+ return ok;
+ }
+
+ bool CSimulatorMsfs2024::requestLightsForSimObject(const CSimConnectObject &simObject)
+ {
+ if (!this->isValidSimObjectNotPendingRemoved(simObject)) { return false; }
+ if (!m_hSimConnect) { return false; }
+
+ // always request, not only when something has changed
+ const SIMCONNECT_DATA_REQUEST_ID requestId = simObject.getRequestId(CSimConnectDefinitions::SimObjectLights);
+ const HRESULT result = this->logAndTraceSendId(
+ SimConnect_RequestDataOnSimObject(m_hSimConnect, requestId,
+ CSimConnectDefinitions::DataRemoteAircraftLights, simObject.getObjectId(),
+ SIMCONNECT_PERIOD_SECOND),
+ simObject, "Cannot request lights data", Q_FUNC_INFO, "SimConnect_RequestDataOnSimObject");
+ return isOk(result);
+ }
+
+ bool CSimulatorMsfs2024::requestModelInfoForSimObject(const CSimConnectObject &simObject)
+ {
+ if (!this->isValidSimObjectNotPendingRemoved(simObject)) { return false; }
+ if (!m_hSimConnect) { return false; }
+
+ // always request, not only when something has changed
+ const SIMCONNECT_DATA_REQUEST_ID requestId = simObject.getRequestId(CSimConnectDefinitions::SimObjectModel);
+ const HRESULT result = this->logAndTraceSendId(
+ SimConnect_RequestDataOnSimObject(m_hSimConnect, requestId,
+ CSimConnectDefinitions::DataRemoteAircraftModelData,
+ simObject.getObjectId(), SIMCONNECT_PERIOD_ONCE),
+ simObject, "Cannot request model info", Q_FUNC_INFO, "SimConnect_RequestDataOnSimObject");
+ return isOk(result);
+ }
+
+ bool CSimulatorMsfs2024::stopRequestingDataForSimObject(const CSimConnectObject &simObject)
+ {
+ if (!simObject.hasValidRequestAndObjectId()) { return false; }
+ if (!m_hSimConnect) { return false; }
+
+ // stop by setting SIMCONNECT_PERIOD_NEVER
+ SIMCONNECT_DATA_REQUEST_ID requestId = simObject.getRequestId(CSimConnectDefinitions::SimObjectPositionData);
+ const HRESULT hr1 = this->logAndTraceSendId(
+ SimConnect_RequestDataOnSimObject(m_hSimConnect, requestId,
+ CSimConnectDefinitions::DataRemoteAircraftGetPosition,
+ simObject.getObjectId(), SIMCONNECT_PERIOD_NEVER),
+ simObject, "Stopping position request", Q_FUNC_INFO, "SimConnect_RequestDataOnSimObject");
+
+ requestId = simObject.getRequestId(CSimConnectDefinitions::SimObjectLights);
+ const HRESULT hr2 = this->logAndTraceSendId(
+ SimConnect_RequestDataOnSimObject(m_hSimConnect, requestId,
+ CSimConnectDefinitions::DataRemoteAircraftLights, simObject.getObjectId(),
+ SIMCONNECT_PERIOD_NEVER),
+ simObject, "Stopping lights request", Q_FUNC_INFO, "SimConnect_RequestDataOnSimObject");
+ return isOk(hr1, hr2);
+ }
+
+ void CSimulatorMsfs2024::initSimulatorInternals()
+ {
+ CSimulatorFsCommon::initSimulatorInternals();
+ m_simulatorInternals.setValue("fsx/simConnectVersion", m_simConnectVersion);
+ }
+
+ void CSimulatorMsfs2024::reset()
+ {
+ this->safeKillTimer();
+
+ // cleared below:
+ // physicallyRemoveAllRemoteAircraft
+ // m_simConnectObjects
+ // m_simConnectObjectsPositionAndPartsTraces
+ // m_addPendingAircraft
+ // m_updateRemoteAircraftInProgress
+ CSimulatorFsCommon::reset(); // clears all pending aircraft etc
+
+ // reset values
+ m_simulatingChangedTs = -1;
+ m_simConnected = false;
+ m_simSimulating = false;
+ m_sbDataReceived = 0;
+ m_requestIdSimObjAircraft = static_cast(RequestSimObjAircraftStart);
+ m_dispatchErrors = 0;
+ m_receiveExceptionCount = 0;
+ m_addedProbes = 0;
+ m_initFsxTerrainProbes = false;
+ m_sendIdTraces.clear();
+ }
+
+ void CSimulatorMsfs2024::clearAllRemoteAircraftData()
+ {
+ const bool reinitProbe =
+ m_useFsxTerrainProbe && m_initFsxTerrainProbes; // re-init if enabled and was initialized
+ this->removeAllProbes();
+
+ // m_addAgainAircraftWhenRemoved cleared below
+ CSimulatorFsCommon::clearAllRemoteAircraftData(); // also removes aircraft
+ m_simConnectObjects.clear();
+ m_addPendingAircraft.clear();
+ m_simConnectObjectsPositionAndPartsTraces.clear();
+
+ if (reinitProbe)
+ {
+ // if we are still alive we re-init the probes
+ QPointer myself(this);
+ QTimer::singleShot(2000, this, [=] {
+ // Shutdown or unloaded
+ if (this->isShuttingDown() || !myself) { return; }
+ m_initFsxTerrainProbes = false; // probes will re-init
+ });
+ }
+ }
+
+ void CSimulatorMsfs2024::onOwnModelChanged(const CAircraftModel &newModel)
+ {
+ m_sbDataReceived = 0;
+ CSimulatorFsCommon::onOwnModelChanged(newModel);
+ }
+
+ QString CSimulatorMsfs2024::fsxPositionToString(const SIMCONNECT_DATA_INITPOSITION &position)
+ {
+ static const QString positionStr(
+ "Lat: %1deg lng: %2deg alt: %3ft pitch: %4deg bank: %5deg hdg: %6deg airspeed: %7kts onGround: %8");
+ return positionStr.arg(position.Latitude)
+ .arg(position.Longitude)
+ .arg(position.Altitude)
+ .arg(position.Pitch)
+ .arg(position.Bank)
+ .arg(position.Heading)
+ .arg(position.Airspeed)
+ .arg(position.OnGround);
+ }
+
+ CCallsignSet CSimulatorMsfs2024::getCallsignsMissingInProvider() const
+ {
+ if (m_simConnectObjects.isEmpty()) { return CCallsignSet(); }
+ const CCallsignSet simObjectCallsigns(m_simConnectObjects.getAllCallsigns(true));
+ const CCallsignSet providerCallsigns(this->getAircraftInRangeCallsigns());
+ return simObjectCallsigns.difference(providerCallsigns);
+ }
+
+ void CSimulatorMsfs2024::traceSendId(const CSimConnectObject &simObject, const QString &functionName,
+ const QString &details, bool forceTrace)
+ {
+ if (!forceTrace && !this->isTracingSendId()) { return; }
+ if (MaxSendIdTraces < 1) { return; } // cppcheck-suppress knownConditionTrueFalse
+ DWORD dwLastId = 0;
+ const HRESULT hr = SimConnect_GetLastSentPacketID(m_hSimConnect, &dwLastId);
+ if (isFailure(hr)) { return; }
+ if (m_sendIdTraces.size() > MaxSendIdTraces) { m_sendIdTraces.removeLast(); }
+ const TraceFsxSendId trace(dwLastId, simObject,
+ details.isEmpty() ? functionName : details % u", " % functionName);
+ m_sendIdTraces.push_front(trace);
+ }
+
+ HRESULT CSimulatorMsfs2024::logAndTraceSendId(HRESULT hr, const QString &warningMsg, const QString &functionName,
+ const QString &functionDetails)
+ {
+ const CSimConnectObject empty;
+ return this->logAndTraceSendId(hr, empty, warningMsg, functionName, functionDetails);
+ }
+
+ HRESULT CSimulatorMsfs2024::logAndTraceSendId(HRESULT hr, const CSimConnectObject &simObject,
+ const QString &warningMsg, const QString &functionName,
+ const QString &functionDetails)
+ {
+ return this->logAndTraceSendId(hr, this->isTracingSendId(), simObject, warningMsg, functionName,
+ functionDetails);
+ }
+
+ HRESULT CSimulatorMsfs2024::logAndTraceSendId(HRESULT hr, bool traceSendId, const CSimConnectObject &simObject,
+ const QString &warningMsg, const QString &functionName,
+ const QString &functionDetails)
+ {
+ if (traceSendId) { this->traceSendId(simObject, functionName, functionDetails); }
+ if (isOk(hr)) { return hr; }
+ if (!warningMsg.isEmpty()) { CLogMessage(this).warning(warningMsg % u" SimObject: " % simObject.toQString()); }
+ this->triggerAutoTraceSendId();
+ return hr;
+ }
+
+ QByteArray CSimulatorMsfs2024::toFsxChar(const QString &string) { return string.toLatin1(); }
+
+ TraceFsxSendId CSimulatorMsfs2024::getSendIdTrace(DWORD sendId) const
+ {
+ for (const TraceFsxSendId &trace : m_sendIdTraces)
+ {
+ if (trace.sendId == sendId) { return trace; }
+ }
+ return TraceFsxSendId::invalid();
+ }
+
+ QString CSimulatorMsfs2024::getSendIdTraceDetails(DWORD sendId) const
+ {
+ const TraceFsxSendId trace = this->getSendIdTrace(sendId);
+ if (trace.sendId == sendId) { return this->getSendIdTraceDetails(trace); }
+ return {};
+ }
+
+ QString CSimulatorMsfs2024::getSendIdTraceDetails(const TraceFsxSendId &trace) const
+ {
+ static const QString d("Send id: %1 obj.id.: %2 SimObj: %3 | '%4'");
+ if (trace.isInvalid()) { return QString(); }
+
+ // update with latest sim object
+ const CSimConnectObject simObject = this->getSimObjectForTrace(trace);
+ return d.arg(trace.sendId).arg(simObject.getObjectId()).arg(simObject.toQString(), trace.comment);
+ }
+
+ int CSimulatorMsfs2024::removeAllProbes()
+ {
+ if (!m_hSimConnect) { return 0; } // already disconnected
+ const QList probes = m_simConnectObjects.getProbes();
+
+ int c = 0;
+ for (const CSimConnectObject &probeSimObject : probes)
+ {
+ if (!probeSimObject.isConfirmedAdded()) { continue; }
+ const SIMCONNECT_DATA_REQUEST_ID requestId =
+ probeSimObject.getRequestId(CSimConnectDefinitions::SimObjectRemove);
+ const HRESULT result = SimConnect_AIRemoveObject(
+ m_hSimConnect, static_cast(probeSimObject.getObjectId()), requestId);
+ if (isOk(result)) { c++; }
+ else
+ {
+ CLogMessage(this).warning(u"Removing probe '%1' from simulator failed") << probeSimObject.getObjectId();
+ }
+ }
+ m_simConnectObjects.removeAllProbes();
+ m_pendingProbeRequests.clear();
+ return c;
+ }
+
+ CSimConnectObject CSimulatorMsfs2024::insertNewSimConnectObject(const CSimulatedAircraft &aircraft, DWORD requestId,
+ CSimConnectObject::SimObjectType type,
+ const CSimConnectObject &removedPendingObject)
+ {
+ if (m_simConnectObjects.contains(aircraft.getCallsign()))
+ {
+ // error, ...?
+ CSimConnectObject &simObject = m_simConnectObjects[aircraft.getCallsign()];
+ simObject.copyAddingFailureCounters(removedPendingObject);
+ simObject.resetTimestampToNow();
+ return simObject;
+ }
+
+ CSimConnectObject simObject;
+ if (m_simConnectObjectsPositionAndPartsTraces.contains(aircraft.getCallsign()))
+ {
+ // if in traces, get the object and reuse it
+ simObject = m_simConnectObjectsPositionAndPartsTraces[aircraft.getCallsign()];
+ m_simConnectObjectsPositionAndPartsTraces.remove(aircraft.getCallsign());
+ simObject.resetState();
+ simObject.setRequestId(requestId);
+ simObject.setAircraft(aircraft);
+ simObject.attachInterpolatorLogger(&m_interpolationLogger); // setting a logger does not start logging
+ }
+ else
+ {
+ simObject = CSimConnectObject(aircraft, requestId, this, this, this->getRemoteAircraftProvider(),
+ &m_interpolationLogger);
+ }
+ simObject.copyAddingFailureCounters(removedPendingObject);
+ simObject.setType(type);
+ m_simConnectObjects.insert(simObject, true); // update timestamp
+ return simObject;
+ }
+
+ const CAltitude &CSimulatorMsfs2024::terrainProbeAltitude()
+ {
+ static const CAltitude alt(50000, CLengthUnit::ft());
+ return alt;
+ }
+
+ QString CSimulatorMsfs2024::fsxCharToQString(const char *fsxChar, int size)
+ {
+ return QString::fromLatin1(fsxChar, size);
+ }
+
+ QString CSimulatorMsfs2024::requestIdToString(DWORD requestId)
+ {
+ if (requestId <= CSimConnectDefinitions::RequestEndMarker)
+ {
+ return CSimConnectDefinitions::requestToString(static_cast(requestId));
+ }
+
+ const CSimConnectDefinitions::SimObjectRequest simRequest = requestToSimObjectRequest(requestId);
+ const CSimConnectObject::SimObjectType simType = CSimConnectObject::requestIdToType(requestId);
+
+ static const QString req("%1 %2 %3");
+ return req.arg(requestId)
+ .arg(CSimConnectObject::typeToString(simType))
+ .arg(CSimConnectDefinitions::simObjectRequestToString(simRequest));
+ }
+
+ DWORD CSimulatorMsfs2024::unitTestRequestId(CSimConnectObject::SimObjectType type)
+ {
+ int start;
+ int end;
+ switch (type)
+ {
+ case CSimConnectObject::TerrainProbe:
+ start = RequestSimObjTerrainProbeStart;
+ end = RequestSimObjTerrainProbeEnd;
+ break;
+ case CSimConnectObject::AircraftNonAtc:
+ case CSimConnectObject::AircraftSimulatedObject:
+ default:
+ start = RequestSimObjAircraftStart;
+ end = RequestSimObjAircraftEnd;
+ break;
+ }
+
+ const int id = CMathUtils::randomInteger(start, end);
+ return static_cast(id);
+ }
+
+ CCallsignSet CSimulatorMsfs2024::physicallyRemoveAircraftNotInProvider()
+ {
+ const CCallsignSet callsignsToBeRemoved(this->getCallsignsMissingInProvider());
+ if (callsignsToBeRemoved.isEmpty()) { return callsignsToBeRemoved; }
+ for (const CCallsign &callsign : callsignsToBeRemoved) { this->physicallyRemoveRemoteAircraft(callsign); }
+
+ if (this->showDebugLogMessage())
+ {
+ this->debugLogMessage(Q_FUNC_INFO,
+ QStringLiteral("CS: '%1'").arg(callsignsToBeRemoved.toStringList().join(", ")));
+ }
+ return callsignsToBeRemoved;
+ }
+
+ void CSimulatorMsfs2024::physicallyRemoveAircraftNotInProviderAsync()
+ {
+ const QPointer myself(this);
+ QTimer::singleShot(100, this, [=] {
+ if (!myself || this->isShuttingDown()) { return; }
+ CSimulatorMsfs2024::physicallyRemoveAircraftNotInProvider();
+ });
+ }
+
+ CSimulatorMsfs2024Listener::CSimulatorMsfs2024Listener(const CSimulatorPluginInfo &info) : ISimulatorListener(info)
+ {
+ m_timer.setInterval(MinQueryIntervalMs);
+ m_timer.setObjectName(this->objectName().append(":m_timer"));
+ connect(&m_timer, &QTimer::timeout, this, &CSimulatorMsfs2024Listener::checkConnection);
+ }
+
+ void CSimulatorMsfs2024Listener::startImpl()
+ {
+ m_simulatorVersion.clear();
+ m_simConnectVersion.clear();
+ m_simulatorName.clear();
+ m_simulatorDetails.clear();
+
+ m_timer.start();
+ }
+
+ void CSimulatorMsfs2024Listener::stopImpl()
+ {
+ m_timer.stop();
+ this->disconnectFromSimulator();
+ }
+
+ void CSimulatorMsfs2024Listener::checkImpl()
+ {
+ if (!m_timer.isActive()) { return; }
+ if (this->isShuttingDown()) { return; }
+
+ QPointer myself(this);
+ QTimer::singleShot(0, this, [=] {
+ if (!myself || !sApp || sApp->isShuttingDown()) { return; }
+ this->checkConnection();
+ });
+
+ // restart because we have just checked now
+ m_timer.start();
+ }
+
+ QString CSimulatorMsfs2024Listener::backendInfo() const
+ {
+ if (m_simulatorName.isEmpty()) { return ISimulatorListener::backendInfo(); }
+ return m_simulatorDetails;
+ }
+
+ void CSimulatorMsfs2024Listener::checkConnection()
+ {
+ Q_ASSERT_X(!CThreadUtils::thisIsMainThread(), Q_FUNC_INFO, "Expect to run in background");
+
+ // check before we access the sim. connection
+ if (this->isShuttingDown() || this->thread()->isInterruptionRequested())
+ {
+ this->stopImpl();
+ return;
+ }
+
+ QElapsedTimer t;
+ t.start();
+ bool check = false;
+ do {
+ // if we can connect, but not dispatch, it can mean a previously started FSX/P3D
+ // blocks remote calls -> RESTART
+ if (!this->connectToSimulator()) { break; }
+
+ // check if we have the right sim.
+ // this check on a remote FSX/P3D not running/existing might TAKE LONG!
+ const HRESULT result =
+ SimConnect_CallDispatch(m_hSimConnect, CSimulatorMsfs2024Listener::SimConnectProc, this);
+
+ // make sure we did not stop in meantime
+ if (this->isShuttingDown() || this->thread()->isInterruptionRequested())
+ {
+ this->stopImpl();
+ return;
+ }
+
+ if (isFailure(result)) { break; } // means serious failure
+ check = this->checkVersionAndSimulator();
+ }
+ while (false);
+
+ this->adjustTimerInterval(t.elapsed());
+ if (check) { emit this->simulatorStarted(this->getPluginInfo()); }
+ }
+
+ void CSimulatorMsfs2024Listener::adjustTimerInterval(qint64 checkTimeMs)
+ {
+ const QString sim = this->getPluginInfo().getSimulatorInfo().toQString(true);
+ CLogMessage(this).debug(u"Checked sim.'%1' connection in %2ms") << sim << checkTimeMs;
+ if (checkTimeMs > qRound(1.25 * MinQueryIntervalMs))
+ {
+ const int newIntervalMs = qRound(1.2 * checkTimeMs / 1000.0) * 1000;
+ CLogMessage(this).debug(u"Check for simulator sim.'%1' connection in %2ms, too slow. Setting %3ms")
+ << sim << checkTimeMs << newIntervalMs;
+ if (m_timer.interval() != newIntervalMs) { m_timer.setInterval(newIntervalMs); }
+ }
+ else
+ {
+ if (m_timer.interval() != MinQueryIntervalMs) { m_timer.setInterval(MinQueryIntervalMs); }
+ }
+
+ // restart
+ m_timer.start();
+ }
+
+ bool CSimulatorMsfs2024Listener::checkVersionAndSimulator() const
+ {
+ const CSimulatorInfo pluginSim(getPluginInfo().getIdentifier());
+ const QString connectedSimName = m_simulatorName.toLower().trimmed();
+
+ if (connectedSimName.isEmpty()) { return false; }
+ if (pluginSim.isP3D())
+ {
+ // P3D drivers only works with P3D
+ return connectedSimName.contains("lockheed") || connectedSimName.contains("martin") ||
+ connectedSimName.contains("p3d") || connectedSimName.contains("prepar");
+ }
+ else if (pluginSim.isFSX())
+ {
+ // FSX drivers only works with FSX
+ return connectedSimName.contains("fsx") || connectedSimName.contains("microsoft") ||
+ connectedSimName.contains("simulator x");
+ }
+ else if (pluginSim.isMSFS())
+ {
+ // MSFS 2020 drivers only works with MSFS
+ return connectedSimName.contains("kittyhawk");
+ }
+ else if (pluginSim.isMSFS2024())
+ {
+ // MSFS2024 drivers only works with MSFS2024
+ return connectedSimName.contains("sunrise");
+ }
+ return false;
+ }
+
+ bool CSimulatorMsfs2024Listener::checkSimConnectDll() const
+ {
+ static const CWinDllUtils::DLLInfo simConnectInfo = CSimConnectUtilities::simConnectDllInfo();
+ if (!simConnectInfo.errorMsg.isEmpty()) { return false; }
+ return true;
+ }
+
+ bool CSimulatorMsfs2024Listener::connectToSimulator()
+ {
+ if (m_simConnected) { return true; }
+ const HRESULT result = SimConnect_Open(&m_hSimConnect, sApp->swiftVersionChar(), nullptr, 0, nullptr, 0);
+ const bool ok = isOk(result);
+ m_simConnected = ok;
+ return ok;
+ }
+
+ bool CSimulatorMsfs2024Listener::disconnectFromSimulator()
+ {
+ if (!m_simConnected) { return false; }
+ SimConnect_Close(m_hSimConnect);
+ m_hSimConnect = nullptr;
+ m_simConnected = false;
+ return true;
+ }
+
+ void CSimulatorMsfs2024Listener::SimConnectProc(SIMCONNECT_RECV *pData, DWORD cbData, void *pContext)
+ {
+ Q_UNUSED(cbData)
+ CSimulatorMsfs2024Listener *simListener = static_cast(pContext);
+ switch (pData->dwID)
+ {
+ case SIMCONNECT_RECV_ID_OPEN:
+ {
+ SIMCONNECT_RECV_OPEN *event = static_cast(pData);
+ simListener->m_simulatorVersion = QStringLiteral("%1.%2.%3.%4")
+ .arg(event->dwApplicationVersionMajor)
+ .arg(event->dwApplicationVersionMinor)
+ .arg(event->dwApplicationBuildMajor)
+ .arg(event->dwApplicationBuildMinor);
+ simListener->m_simConnectVersion = QStringLiteral("%1.%2.%3.%4")
+ .arg(event->dwSimConnectVersionMajor)
+ .arg(event->dwSimConnectVersionMinor)
+ .arg(event->dwSimConnectBuildMajor)
+ .arg(event->dwSimConnectBuildMinor);
+ simListener->m_simulatorName = CSimulatorMsfs2024::fsxCharToQString(event->szApplicationName);
+ simListener->m_simulatorDetails = QStringLiteral("Name: '%1' Version: %2 SimConnect: %3")
+ .arg(simListener->m_simulatorName, simListener->m_simulatorVersion,
+ simListener->m_simConnectVersion);
+ const CStatusMessage msg = CStatusMessage(simListener).info(u"Connect to %1: '%2'")
+ << simListener->getPluginInfo().getIdentifier() << simListener->backendInfo();
+
+ // avoid the same message over and over again
+ if (msg.getMessage() != simListener->m_lastMessage.getMessage())
+ {
+ CLogMessage::preformatted(msg);
+ simListener->m_lastMessage = msg;
+ }
+ break;
+ }
+ case SIMCONNECT_RECV_ID_EXCEPTION: break;
+ default: break;
+ }
+ }
+} // namespace swift::simplugin::msfs2024common
diff --git a/src/plugins/simulator/msfs2024/simulatormsfs2024common.h b/src/plugins/simulator/msfs2024/simulatormsfs2024common.h
new file mode 100644
index 000000000..464d2485a
--- /dev/null
+++ b/src/plugins/simulator/msfs2024/simulatormsfs2024common.h
@@ -0,0 +1,817 @@
+// SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors
+// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
+
+//! \file
+
+#ifndef SWIFT_SIMPLUGIN_MSFS2024COMMON_SIMULATORMSFS2024COMMON_H
+#define SWIFT_SIMPLUGIN_MSFS2024COMMON_SIMULATORMSFS2024COMMON_H
+
+#include
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include "config/buildconfig.h"
+#include "core/simulator.h"
+#include "gui/components/dbmappingcomponentaware.h"
+#include "misc/aviation/airportlist.h"
+#include "misc/aviation/altitude.h"
+#include "misc/datacache.h"
+#include "misc/network/client.h"
+#include "misc/pixmap.h"
+#include "misc/pq/frequency.h"
+#include "misc/simulation/aircraftmodel.h"
+#include "misc/simulation/data/modelcaches.h" // TODO ???
+#include "misc/simulation/interpolation/interpolatorlinear.h"
+#include "misc/simulation/settings/simulatorsettings.h"
+#include "misc/simulation/simulatedaircraft.h"
+#include "misc/simulation/simulatorplugininfo.h"
+#include "misc/statusmessage.h"
+#include "plugins/simulator/fscommon/simulatorfscommon.h"
+#include "plugins/simulator/msfs2024/msfs2024export.h"
+#include "plugins/simulator/msfs2024/simconnectdatadefinitionmsfs2024.h"
+#include "plugins/simulator/msfs2024/simconnectobjectmsfs2024.h"
+#include "plugins/simulator/msfs2024/simconnectsymbolsmsfs2024.h"
+#include "plugins/simulator/msfs2024/simconnectwindowsmsfs2024.h"
+
+namespace swift::simplugin::msfs2024common
+{
+ //! SimConnect Event IDs
+ enum EventIds
+ {
+ SystemEventSimStatus,
+ SystemEventObjectAdded,
+ SystemEventObjectRemoved,
+ SystemEventSlewToggle,
+ SystemEventFrame,
+ SystemEventPause,
+ SystemEventFlightLoaded,
+ EventPauseToggle,
+ EventFreezeLatLng,
+ EventFreezeAlt,
+ EventFreezeAtt,
+ EventSetCom1Active,
+ EventSetCom2Active,
+ EventSetCom1Standby,
+ EventSetCom2Standby,
+ EventSetTransponderCode,
+ EventTextMessage,
+ EventSetTimeZuluYear,
+ EventSetTimeZuluDay,
+ EventSetTimeZuluHours,
+ EventSetTimeZuluMinutes,
+ // ------------ lights -------------
+ EventLandingLightsOff,
+ EventLandinglightsOn,
+ EventLandingLightsSet,
+ EventLandingLightsToggle,
+ EventPanelLightsOff,
+ EventPanelLightsOn,
+ EventPanelLightsSet,
+ EventStrobesOff,
+ EventStrobesOn,
+ EventStrobesSet,
+ EventStrobesToggle,
+ EventToggleBeaconLights,
+ EventToggleCabinLights,
+ EventToggleLogoLights,
+ EventToggleNavLights,
+ EventToggleRecognitionLights,
+ EventToggleTaxiLights,
+ EventToggleWingLights,
+ // ------------- flaps -------------
+ EventFlapsSet,
+ // ---------- end marker -----------
+ EventFSXEndMarker
+ };
+
+ //! Struct to trace send ids
+ struct TraceFsxSendId
+ {
+ //! Ctor
+ TraceFsxSendId(DWORD sendId, const CSimConnectObject &simObject, const QString &comment)
+ : sendId(sendId), simObject(simObject), comment(comment)
+ {}
+
+ // DWORD is unsigned
+ DWORD sendId = 0; //!< the send id
+ CSimConnectObject simObject; //!< CSimConnectObject at the time of the trace
+ QString comment; //!< where sent
+
+ //! For probe
+ bool isForProbe() const { return simObject.getType() == CSimConnectObject::TerrainProbe; }
+
+ //! For aircraft
+ bool isForAircraft() const { return simObject.getType() == CSimConnectObject::AircraftNonAtc; }
+
+ //! Invalid trace?
+ bool isInvalid() const { return sendId == 0 && simObject.isInvalid() == 0 && comment.isEmpty(); }
+
+ //! Valid trace?
+ bool isValid() const { return !this->isInvalid(); }
+
+ //! Invalid object
+ static const TraceFsxSendId &invalid()
+ {
+ static const TraceFsxSendId i(0, CSimConnectObject(), "");
+ return i;
+ }
+ };
+
+ //! Buffer for models read from SimConnect
+ struct sSimObjectLivery
+ {
+ QString szSimObjectCombinedTitle;
+ QString szSimObjectTitle;
+ QString szLiveryName;
+ };
+
+ //! Flags for the load status of reading from the SimConnect
+ struct sSimmobjectLoaded
+ {
+ bool bLoadStarted = false;
+ bool bAirplaneLoaded = false;
+ bool bHelicopterLoaded = false;
+ bool bHotAirLoaded = false;
+ };
+
+ //! FSX Simulator Implementation
+ class MSFS2024_EXPORT CSimulatorMsfs2024 : public fscommon::CSimulatorFsCommon
+ {
+ Q_OBJECT
+ Q_INTERFACES(swift::core::ISimulator)
+ Q_INTERFACES(swift::misc::simulation::ISimulationEnvironmentProvider)
+ Q_INTERFACES(swift::misc::simulation::IInterpolationSetupProvider)
+
+ public:
+ //! Constructor, parameters as in \sa swift::core::ISimulatorFactory::create
+ CSimulatorMsfs2024(const swift::misc::simulation::CSimulatorPluginInfo &info,
+ swift::misc::simulation::IOwnAircraftProvider *ownAircraftProvider,
+ swift::misc::simulation::IRemoteAircraftProvider *remoteAircraftProvider,
+ swift::misc::network::IClientProvider *clientProvider, QObject *parent = nullptr);
+
+ //! Destructor
+ virtual ~CSimulatorMsfs2024() override;
+
+ //! \name ISimulator implementations
+ //! @{
+ virtual bool connectTo() override;
+ virtual bool disconnectFrom() override;
+ virtual bool
+ physicallyAddRemoteAircraft(const swift::misc::simulation::CSimulatedAircraft &newRemoteAircraft) override;
+ virtual bool physicallyRemoveRemoteAircraft(const swift::misc::aviation::CCallsign &callsign) override;
+ virtual int physicallyRemoveAllRemoteAircraft() override;
+ virtual bool updateOwnSimulatorCockpit(const swift::misc::simulation::CSimulatedAircraft &ownAircraft,
+ const swift::misc::CIdentifier &originator) override;
+ virtual bool updateOwnSimulatorSelcal(const swift::misc::aviation::CSelcal &selcal,
+ const swift::misc::CIdentifier &originator) override;
+ virtual void displayStatusMessage(const swift::misc::CStatusMessage &message) const override;
+ virtual void displayTextMessage(const swift::misc::network::CTextMessage &message) const override;
+ virtual bool isPhysicallyRenderedAircraft(const swift::misc::aviation::CCallsign &callsign) const override;
+ virtual swift::misc::aviation::CCallsignSet physicallyRenderedAircraft() const override;
+ virtual swift::misc::CStatusMessageList debugVerifyStateAfterAllAircraftRemoved() const override;
+ virtual QString getStatisticsSimulatorSpecific() const override;
+ virtual void resetAircraftStatistics() override;
+ virtual void setFlightNetworkConnected(bool connected) override;
+ virtual swift::misc::CStatusMessageList
+ getInterpolationMessages(const swift::misc::aviation::CCallsign &callsign) const override;
+ virtual bool testSendSituationAndParts(const swift::misc::aviation::CCallsign &callsign,
+ const swift::misc::aviation::CAircraftSituation &situation,
+ const swift::misc::aviation::CAircraftParts &parts) override;
+ //! @}
+
+ //! \copydoc swift::misc::simulation::ISimulationEnvironmentProvider::requestElevation
+ //! \remark x86 FSX version, x64 version is overridden
+ //! \sa CSimulatorFsxCommon::is
+ virtual bool requestElevation(const swift::misc::geo::ICoordinateGeodetic &reference,
+ const swift::misc::aviation::CCallsign &aircraftCallsign) override;
+
+ //! saves the SimObjects received from the simulator a structure
+ void CacheSimObjectAndLiveries(const SIMCONNECT_RECV_ENUMERATE_SIMOBJECT_AND_LIVERY_LIST *msg);
+
+ //! Write the sim objects and liveries to file and give back the number of objects written
+ int writeSimObjectsAndLiveriesToFile(const swift::misc::simulation::CAircraftModelList Modelset);
+
+ //! creates a new model list
+ void createNewModelList();
+
+ //! resets the sim object and liveries cache
+ void setSimObjectAndLiveries();
+
+ //! Tracing right now?
+ bool isTracingSendId() const;
+
+ //! Trace enable (can be auto enable also)
+ bool isTraceSendId() const { return m_traceSendId; }
+
+ //! Set tracing on/off
+ void setTractingSendId(bool trace);
+
+ //! FSX Terrain probe
+ //! \remark must be off at P3D v4.2 drivers or later
+ bool isUsingFsxTerrainProbe() const { return m_useFsxTerrainProbe; }
+
+ //! FSX terrain probe
+ void setUsingFsxTerrainProbe(bool use) { m_useFsxTerrainProbe = use; }
+
+ //! Using the SB offsets?
+ bool isUsingSbOffsetValues() const { return m_useSbOffsets; }
+
+ //! Use SB offset values
+ void setUsingSbOffsetValues(bool use);
+
+ //! Number of received SB4 packets
+ //! \remark if this is increasing, SB4 is supported
+ int receivedSBPackets() const { return m_sbDataReceived; }
+
+ //! Allow adding as simulated object instead of non ATC
+ bool isAddingAsSimulatedObjectEnabled() const { return m_useAddSimulatedObj; }
+
+ //! Allow adding as simulated object instead of non ATC
+ void setAddingAsSimulatedObjectEnabled(bool enabled);
+
+ //! Request for sim data (request in range of sim data)?
+ static bool isRequestForSimObjAircraft(DWORD requestId)
+ {
+ return requestId >= RequestSimObjAircraftStart && requestId <= RequestSimObjAircraftRangeEnd;
+ }
+
+ //! Request for probe (elevation)?
+ static bool isRequestForSimObjTerrainProbe(DWORD requestId)
+ {
+ return requestId >= RequestSimObjTerrainProbeStart && requestId <= RequestSimObjTerrainProbeRangeEnd;
+ }
+
+ //! Request for any CSimConnectObject?
+ static bool isRequestForSimConnectObject(DWORD requestId)
+ {
+ return isRequestForSimObjAircraft(requestId) || isRequestForSimObjTerrainProbe(requestId);
+ }
+
+ //! Sub request type
+ static CSimConnectDefinitions::SimObjectRequest requestToSimObjectRequest(DWORD requestId);
+
+ //! Random unit text request id
+ //! \private
+ static DWORD unitTestRequestId(CSimConnectObject::SimObjectType type);
+
+ //! Encapsulates creating QString from FSX string data
+ static QString fsxCharToQString(const char *fsxChar, int size = -1);
+
+ protected:
+ //! SimConnect callback
+ //! \note all tasks called in this function (i.e, all called functions) must perform fast or shall be called
+ //! asynchronously
+ static void CALLBACK SimConnectProc(SIMCONNECT_RECV *pData, DWORD cbData, void *pContext);
+
+ //! \name Interface implementations
+ //! @{
+ virtual bool isConnected() const override;
+ virtual bool isSimulating() const override;
+ //! @}
+
+ //! \name Base class overrides
+ //! @{
+ virtual void reset() override;
+ virtual void initSimulatorInternals() override;
+ virtual void clearAllRemoteAircraftData() override;
+ virtual void onOwnModelChanged(const swift::misc::simulation::CAircraftModel &newModel) override;
+ //! @}
+
+ //! Timer event (our SimConnect event loop), runs dispatch
+ //! \sa m_timerId
+ //! \sa CSimulatorFsxCommon::dispatch
+ virtual void timerEvent(QTimerEvent *event) override;
+
+ //! Specific P3D events
+ virtual HRESULT initEventsP3D();
+
+ //! \ingroup swiftdotcommands
+ //!
+ //! .drv sendid on|off tracing simConnect sendId on/off
+ //!
+ virtual bool parseDetails(const swift::misc::CSimpleCommandParser &parser) override;
+
+ //! Trigger tracing ids for some while
+ //! \sa CSimulatorFsxCommon::isTracingSendId
+ bool triggerAutoTraceSendId(qint64 traceTimeMs = AutoTraceOffsetMs);
+
+ //! Callsign for pending request
+ swift::misc::aviation::CCallsign getCallsignForPendingProbeRequests(DWORD requestId, bool remove);
+
+ //! Get new request id, overflow safe
+ SIMCONNECT_DATA_REQUEST_ID obtainRequestIdForSimObjAircraft();
+
+ //! Get new request id, overflow safe
+ SIMCONNECT_DATA_REQUEST_ID obtainRequestIdForSimObjTerrainProbe();
+
+ //! Release AI control
+ //! \remark P3D version is overridden
+ virtual bool releaseAIControl(const CSimConnectObject &simObject, SIMCONNECT_DATA_REQUEST_ID requestId);
+
+ //! Valid CSimConnectObject which is NOT pendig removed
+ bool isValidSimObjectNotPendingRemoved(const CSimConnectObject &simObject) const;
+
+ //! CSimConnectObject for trace
+ CSimConnectObject getSimObjectForTrace(const TraceFsxSendId &trace) const;
+
+ //! Remove the CSimConnectObject linked in the trace
+ bool removeSimObjectForTrace(const TraceFsxSendId &trace);
+
+ //! Remove camera if any
+ virtual void removeCamera(CSimConnectObject &simObject);
+
+ //! Remove observer if any
+ virtual void removeObserver(CSimConnectObject &simObject);
+
+ //! Trace if required, log errors
+ HRESULT logAndTraceSendId(HRESULT hr, const QString &warningMsg, const QString &functionName,
+ const QString &functionDetails = {});
+
+ //! Trace if required, log errors
+ HRESULT logAndTraceSendId(HRESULT hr, const CSimConnectObject &simObject, const QString &warningMsg,
+ const QString &functionName, const QString &functionDetails = {});
+
+ //! Trace if required, log errors
+ HRESULT logAndTraceSendId(HRESULT hr, bool traceSendId, const CSimConnectObject &simObject,
+ const QString &warningMsg, const QString &functionName,
+ const QString &functionDetails = {});
+
+ //! Convert to FSX char array
+ static QByteArray toFsxChar(const QString &string);
+
+ //! Register help
+ static void registerHelp();
+
+ //! @{
+ //! Word size
+ static bool is32bit() { return (swift::config::CBuildConfig::buildWordSize() == 32); }
+ static bool is64bit() { return (swift::config::CBuildConfig::buildWordSize() == 64); }
+ //! @}
+
+ //! Format conversion
+ //! \note must be valid situation
+ static SIMCONNECT_DATA_INITPOSITION
+ aircraftSituationToFsxPosition(const swift::misc::aviation::CAircraftSituation &situation, bool sendGnd = true,
+ bool forceUnderflowDetection = false,
+ swift::misc::CStatusMessage *details = nullptr);
+
+ //! Format conversion
+ //! \note must be valid situation
+ static SIMCONNECT_DATA_PBH
+ aircraftSituationToFsxPBH(const swift::misc::aviation::CAircraftSituation &situation);
+
+ //! Format conversion
+ static SIMCONNECT_DATA_INITPOSITION
+ coordinateToFsxPosition(const swift::misc::geo::ICoordinateGeodetic &coordinate);
+
+ //! Format conversion
+ static SIMCONNECT_DATA_LATLONALT
+ coordinateToFsxLatLonAlt(const swift::misc::geo::ICoordinateGeodetic &coordinate);
+
+ //! Valid FSX/P3D position
+ static bool isValidFsxPosition(const SIMCONNECT_DATA_INITPOSITION &fsxPos);
+
+ //! Valid 180degrees value
+ static bool isValid180Deg(double deg) { return deg > -180.0 && deg <= 180.0; }
+
+ static constexpr qint64 AutoTraceOffsetMs = 10 * 1000; //!< how long do we trace?
+ HANDLE m_hSimConnect = nullptr; //!< handle to SimConnect object
+ DispatchProc m_dispatchProc = &CSimulatorMsfs2024::SimConnectProc; //!< called function for dispatch, can be
+ //!< overriden by specialized P3D function
+ CSimConnectObjects m_simConnectObjects; //!< AI objects and their object and request ids
+
+ // probes
+ bool m_useFsxTerrainProbe = is32bit(); //!< Use FSX Terrain probe?
+ bool m_initFsxTerrainProbes = false; //!< initialized terrain probes
+ int m_addedProbes = 0; //!< added probes
+ QMap
+ m_pendingProbeRequests; //!< pending elevation requests: requestId/aircraft callsign
+
+ swift::misc::physical_quantities::CLength m_altitudeDelta; //!< FS2020 effect of temperature on altitude
+
+ private:
+ //! Reason for adding an aircraft
+ enum AircraftAddMode
+ {
+ ExternalCall, //!< normal external request to add aircraft
+ AddByTimer, //!< add pending aircraft by timer
+ AddAfterAdded, //!< add pending because object successfully added
+ AddedAfterRemoved //!< added again after removed
+ };
+
+ //! Mode as string
+ static const QString &modeToString(AircraftAddMode mode);
+
+ //! Dispatch SimConnect messages
+ //! \remark very frequently called
+ void dispatch();
+
+ //! Implementation of add remote aircraft, which also handles FSX specific adding one by one
+ //! \remark main purpose of this function is to only add one aircraft at a time, and only if simulator is not
+ //! paused/stopped
+ bool physicallyAddRemoteAircraftImpl(const swift::misc::simulation::CSimulatedAircraft &newRemoteAircraft,
+ AircraftAddMode addMode,
+ const CSimConnectObject &correspondingSimObject = {});
+
+ //! Add AI object for terrain probe
+ //! \remark experimental
+ bool physicallyAddAITerrainProbe(const swift::misc::geo::ICoordinateGeodetic &coordinate, int number);
+
+ //! Add number probes (inits the probe objects)
+ //! \remark experimental
+ int physicallyInitAITerrainProbes(const swift::misc::geo::ICoordinateGeodetic &coordinate, int number);
+
+ //! Remove aircraft no longer in provider
+ //! \remark kind of cleanup function, in an ideal scenario this should never need to cleanup something
+ swift::misc::aviation::CCallsignSet physicallyRemoveAircraftNotInProvider();
+
+ //! ASynchronous version of physicallyRemoveAircraftNotInProvider
+ void physicallyRemoveAircraftNotInProviderAsync();
+
+ //! Verify that an object has been added in simulator
+ //! \remark checks if the object was really added after an "add request" and not directly removed again
+ //! \remark requests further data on remote aircraft (lights, ..) when correctly added
+ void verifyAddedRemoteAircraft(const swift::misc::simulation::CSimulatedAircraft &remoteAircraftIn);
+
+ //! Adding an aircraft failed
+ void addingAircraftFailed(const CSimConnectObject &simObject);
+
+ //! Create a detailed info about the failed aircraft
+ bool verifyFailedAircraftInfo(const CSimConnectObject &simObject, swift::misc::CStatusMessage &details) const;
+
+ //! Logging version of verifyFailedAircraftInfo
+ bool logVerifyFailedAircraftInfo(const CSimConnectObject &simObject) const;
+
+ //! Verify the probe
+ void verifyAddedTerrainProbe(const swift::misc::simulation::CSimulatedAircraft &remoteAircraftIn);
+
+ //! Add next aircraft based on timer
+ void timerBasedObjectAddOrRemove();
+
+ //! Add next aircraft after another has been confirmed
+ void addPendingAircraftAfterAdded();
+
+ //! Try to add the next aircraft (one by one)
+ void addPendingAircraft(AircraftAddMode mode);
+
+ //! Remove as m_addPendingAircraft and m_aircraftToAddAgainWhenRemoved
+ CSimConnectObject removeFromAddPendingAndAddAgainAircraft(const swift::misc::aviation::CCallsign &callsign);
+
+ //! Call this method to declare the simulator connected
+ void setSimConnected();
+
+ //! Called when simulator has started
+ void onSimRunning();
+
+ //! Deferred version of onSimRunning to avoid jitter
+ void onSimRunningDeferred(qint64 referenceTs);
+
+ //! Called every visual frame
+ void onSimFrame();
+
+ //! Called when simulator has stopped, e.g. by selecting the "select aircraft screen"
+ void onSimStopped();
+
+ //! Simulator is going down
+ void onSimExit();
+
+ //! Init when connected
+ HRESULT initWhenConnected();
+
+ //! Initialize SimConnect system events
+ HRESULT initEvents();
+
+ //! Initialize SimConnect data definitions
+ HRESULT initDataDefinitionsWhenConnected();
+
+ //! Update remote aircraft
+ //! \remark this is where the interpolated data are sent
+ void updateRemoteAircraft();
+
+ //! Update remote aircraft parts (send to FSX)
+ bool updateRemoteAircraftParts(const CSimConnectObject &simObject,
+ const swift::misc::simulation::CInterpolationResult &result, bool forcedUpdate);
+
+ //! Send parts to simulator
+ //! \remark does not send if there is no change
+ bool sendRemoteAircraftPartsToSimulator(const CSimConnectObject &simObject,
+ const swift::misc::aviation::CAircraftParts &parts);
+
+ //! Send ATC data (callsign etc.) to simulator
+ bool sendRemoteAircraftAtcDataToSimulator(const CSimConnectObject &simObject);
+
+ //! Send lights to simulator (those which have to be toggled)
+ //! \remark challenge here is that I can only sent those value if I have already obtained the current light
+ //! state from simulator \param force send lights even if they appear to be the same
+ void sendToggledLightsToSimulator(const CSimConnectObject &simObject,
+ const swift::misc::aviation::CAircraftLights &lightsWanted,
+ bool force = false);
+
+ //! Call CSimulatorFsxCommon::updateRemoteAircraftFromSimulator asynchronously
+ //! \remark do not to send SimConnect data in event loop
+ void triggerUpdateRemoteAircraftFromSimulator(const CSimConnectObject &simObject,
+ const DataDefinitionPosData &remoteAircraftData);
+
+ //! Call CSimulatorFsxCommon::updateRemoteAircraftFromSimulator asynchronously
+ //! \remark do not to send SimConnect data in event loop
+ void triggerUpdateRemoteAircraftFromSimulator(const CSimConnectObject &simObject,
+ const DataDefinitionRemoteAircraftModel &remoteAircraftModel);
+
+ //! Remote aircraft data sent from simulator
+ void updateRemoteAircraftFromSimulator(const CSimConnectObject &simObject,
+ const DataDefinitionPosData &remoteAircraftData);
+
+ //! Remote aircraft data sent from simulator
+ void updateRemoteAircraftFromSimulator(const CSimConnectObject &simObject,
+ const DataDefinitionRemoteAircraftModel &remoteAircraftModel);
+
+ //! Probe data sent from simulator
+ void updateProbeFromSimulator(const swift::misc::aviation::CCallsign &callsign,
+ const DataDefinitionPosData &remoteAircraftData);
+
+ //! Customization point for adjusting altitude to compensate for temperature effect
+ virtual void
+ setTrueAltitude(swift::misc::aviation::CAircraftSituation &aircraftSituation,
+ const swift::simplugin::msfs2024common::DataDefinitionOwnAircraft &simulatorOwnAircraft);
+
+ //! Called when data about our own aircraft are received
+ void updateOwnAircraftFromSimulator(const DataDefinitionOwnAircraft &simulatorOwnAircraft);
+
+ //! Update from SB client area
+ //! \threadsafe
+ void updateOwnAircraftFromSimulator(const DataDefinitionClientAreaSb &sbDataArea);
+
+ //! Update transponder mode
+ //! \threadsafe
+ void updateTransponderMode(const misc::aviation::CTransponder::TransponderMode xpdrMode);
+
+ //! Update transponder mode from MSFS
+ void updateMSFSTransponderMode(const DataDefinitionMSFSTransponderMode transponderMode);
+
+ //! An AI aircraft was added in the simulator
+ bool simulatorReportedObjectAdded(DWORD objectId);
+
+ //! Simulator reported that AI aircraft was removed
+ bool simulatorReportedObjectRemoved(DWORD objectID);
+
+ //! Set ID of a SimConnect object, so far we only have an request id in the object
+ bool setSimConnectObjectId(DWORD requestId, DWORD objectId);
+
+ //! Remember current lights
+ bool setCurrentLights(const swift::misc::aviation::CCallsign &callsign,
+ const swift::misc::aviation::CAircraftLights &lights);
+
+ //! Remember lights sent
+ bool setLightsAsSent(const swift::misc::aviation::CCallsign &callsign,
+ const swift::misc::aviation::CAircraftLights &lights);
+
+ //! Display receive exceptions?
+ bool stillDisplayReceiveExceptions();
+
+ //! The SimConnect related objects
+ const CSimConnectObjects &getSimConnectObjects() const { return m_simConnectObjects; }
+
+ //! The SimConnect object for idxs
+ CSimConnectObject getSimObjectForObjectId(DWORD objectId) const;
+
+ //! Request data for a CSimConnectObject (aka remote aircraft)
+ bool requestPositionDataForSimObject(const CSimConnectObject &simObject,
+ SIMCONNECT_PERIOD period = SIMCONNECT_PERIOD_SECOND);
+
+ //! Request data for the terrain probe
+ bool requestTerrainProbeData(const CSimConnectObject &simObject,
+ const swift::misc::aviation::CCallsign &aircraftCallsign);
+
+ //! Request lights for a CSimConnectObject
+ bool requestLightsForSimObject(const CSimConnectObject &simObject);
+
+ //! Model info for a CSimConnectObject
+ bool requestModelInfoForSimObject(const CSimConnectObject &simObject);
+
+ //! Stop requesting data for CSimConnectObject
+ bool stopRequestingDataForSimObject(const CSimConnectObject &simObject);
+
+ //! FSX position as string
+ static QString fsxPositionToString(const SIMCONNECT_DATA_INITPOSITION &position);
+
+ //! Get the callsigns which are no longer in the provider, but still in m_simConnectObjects
+ swift::misc::aviation::CCallsignSet getCallsignsMissingInProvider() const;
+
+ //! Set tracing on/off
+ void setTraceSendId(bool traceSendId) { m_traceSendId = traceSendId; }
+
+ //! Trace the send id
+ void traceSendId(const CSimConnectObject &simObject, const QString &functionName, const QString &details = {},
+ bool forceTrace = false);
+
+ //! Send id trace or given send id
+ TraceFsxSendId getSendIdTrace(DWORD sendId) const;
+
+ //! Get the trace details, otherwise empty string
+ QString getSendIdTraceDetails(const TraceFsxSendId &trace) const;
+
+ //! Get the trace details, otherwise empty string
+ QString getSendIdTraceDetails(DWORD sendId) const;
+
+ //! Remove all probes
+ int removeAllProbes();
+
+ //! Insert a new SimConnect object
+ CSimConnectObject insertNewSimConnectObject(const swift::misc::simulation::CSimulatedAircraft &aircraft,
+ DWORD requestId, CSimConnectObject::SimObjectType type,
+ const CSimConnectObject &removedPendingObject = {});
+
+ //! Update simulator COM from swift data. Returns true if simulator frequency was changed
+ bool updateCOMFromSwiftToSimulator(const swift::misc::physical_quantities::CFrequency &newFreq,
+ const swift::misc::physical_quantities::CFrequency &lastSimFreq,
+ swift::misc::physical_quantities::CFrequency &last25kHzSimFreq, EventIds id);
+
+ //! Used for terrain probes
+ static const swift::misc::aviation::CAltitude &terrainProbeAltitude();
+
+ static constexpr int GuessRemoteAircraftPartsCycle = 20; //!< guess every n-th cycle
+ static constexpr int SkipUpdateCyclesForCockpit = 10; //!< skip x cycles before updating cockpit again
+ static constexpr int IgnoreReceiveExceptions = 10; //!< skip exceptions when displayed more than x times
+ static constexpr int MaxSendIdTraces = 10000; //!< max.traces of send id
+ static constexpr DWORD MaxSimObjAircraft = 10000; //!< max.number of SimObjects at the same time
+ static constexpr DWORD MaxSimObjProbes = 100; //!< max. probes
+
+ // -- second chance tresholds --
+ static constexpr int ThresholdAddException = 1; //!< one failure allowed
+ static constexpr int ThresholdAddedAndDirectlyRemoved = 2; //!< two failures allowed
+
+ // -- range for sim data, each sim object will get its own request id and use the offset ranges
+ static constexpr int RequestSimObjAircraftStart = static_cast(CSimConnectDefinitions::RequestEndMarker);
+ static constexpr int RequestSimObjAircraftEnd = RequestSimObjAircraftStart - 1 + MaxSimObjAircraft;
+ static constexpr int RequestSimObjAircraftRangeEnd =
+ RequestSimObjAircraftStart - 1 +
+ static_cast(CSimConnectDefinitions::SimObjectEndMarker) * MaxSimObjAircraft;
+
+ // -- range for probe data, each probe object will get its own request id and use the offset ranges
+ static constexpr int RequestSimObjTerrainProbeStart = RequestSimObjAircraftRangeEnd + 1;
+ static constexpr int RequestSimObjTerrainProbeEnd = RequestSimObjTerrainProbeStart - 1 + MaxSimObjProbes;
+ static constexpr int RequestSimObjTerrainProbeRangeEnd =
+ RequestSimObjTerrainProbeStart - 1 +
+ static_cast(CSimConnectDefinitions::SimObjectEndMarker) * MaxSimObjProbes;
+
+ // times
+ static constexpr int AddPendingAircraftIntervalMs = 20 * 1000;
+ static constexpr int DispatchIntervalMs = 10; //!< how often with run the FSX event queue
+ static constexpr int DeferSimulatingFlagMs =
+ 1500; //!< simulating can jitter at startup (simulating->stopped->simulating, multiple start events), so we
+ //!< defer detection
+ static constexpr int DeferResendingLights =
+ 2500; //!< Resend light state when aircraft light state was not yet available
+
+ QString m_simConnectVersion; //!< SimConnect version
+ bool m_simConnected = false; //!< Is simulator connected?
+ bool m_simSimulating = false; //!< Simulator running?
+ bool m_useSbOffsets = true; //!< with SB offsets
+ bool m_logSbOffsets = false; //!< log SB offsets
+ bool m_traceSendId = false; //!< trace the send ids, meant for debugging
+ bool m_useAddSimulatedObj = false; //!< simulated object use if AI Non ATC object fails
+ qint64 m_traceAutoUntilTs = -1; //!< allows to automatically trace for some time
+ qint64 m_simulatingChangedTs = -1; //!< timestamp, when simulating changed (used to avoid jitter)
+ int m_sbDataReceived = 0; //!< SB3 area data received
+
+ // tracing dispatch performance
+ int m_dispatchErrors = 0; //!< number of dispatched failed, \sa dispatch
+ int m_dispatchProcCount = 0; //!< number of dispatchProc counts
+ int m_dispatchProcEmptyCount = 0; //!< number dispatchProc doing nothing
+ qint64 m_dispatchTimeMs = -1; //!< \sa ISimulator::getStatisticsSimulatorSpecific
+ qint64 m_dispatchMaxTimeMs = -1; //!< \sa ISimulator::getStatisticsSimulatorSpecific
+ qint64 m_dispatchProcTimeMs = -1; //!< \sa ISimulator::getStatisticsSimulatorSpecific
+ qint64 m_dispatchProcMaxTimeMs = -1; //!< \sa ISimulator::getStatisticsSimulatorSpecific
+
+ SIMCONNECT_RECV_ID m_dispatchReceiveIdLast = SIMCONNECT_RECV_ID_NULL; //!< last receive id from dispatching
+ SIMCONNECT_RECV_ID m_dispatchReceiveIdMaxTime =
+ SIMCONNECT_RECV_ID_NULL; //!< receive id corresponding to max.time
+ DWORD m_dispatchRequestIdLast =
+ CSimConnectDefinitions::RequestEndMarker; //!< request id if any for last request
+ DWORD m_dispatchRequestIdMaxTime =
+ CSimConnectDefinitions::RequestEndMarker; //!< request id corresponding to max.time
+
+ // sending via SimConnect
+ QList m_sendIdTraces; //!< Send id traces for debugging, latest first
+ int m_receiveExceptionCount = 0; //!< exceptions
+ int m_requestSimObjectDataCount = 0; //!< requested SimObjects
+
+ // settings
+ swift::misc::simulation::settings::CMultiSimulatorDetailsSettings m_detailsSettings;
+
+ // objects
+ CSimConnectObjects m_simConnectObjectsPositionAndPartsTraces; //!< position/parts received, but object not yet
+ //!< added, excluded, disabled etc.
+ CSimConnectObjects m_addPendingAircraft; //!< aircraft/probes awaiting to be added;
+ SIMCONNECT_DATA_REQUEST_ID m_requestIdSimObjAircraft = static_cast(
+ RequestSimObjAircraftStart); //!< request id, use obtainRequestIdForSimObjAircraft to get id
+ SIMCONNECT_DATA_REQUEST_ID m_requestIdSimObjTerrainProbe = static_cast(
+ RequestSimObjTerrainProbeStart); //!< request id, use obtainRequestIdForSimObjTerrainProbe to get id
+ QTimer m_simObjectTimer; //!< updating of SimObjects awaiting to be added
+
+ // Last selected frequencies in simulator before setting 8.33 kHz spacing frequency
+ swift::misc::physical_quantities::CFrequency m_lastCom1Active {
+ 0, swift::misc::physical_quantities::CFrequencyUnit::nullUnit()
+ }; //!< last COM1 active frequency
+ swift::misc::physical_quantities::CFrequency m_lastCom1Standby {
+ 0, swift::misc::physical_quantities::CFrequencyUnit::nullUnit()
+ }; //!< last COM1 standby frequency
+ swift::misc::physical_quantities::CFrequency m_lastCom2Active {
+ 0, swift::misc::physical_quantities::CFrequencyUnit::nullUnit()
+ }; //!< last COM2 active frequency
+ swift::misc::physical_quantities::CFrequency m_lastCom2Standby {
+ 0, swift::misc::physical_quantities::CFrequencyUnit::nullUnit()
+ }; //!< last COM2 standby frequency
+
+ //! Request id to string
+ static QString requestIdToString(DWORD requestId);
+
+ //! status of loaded sim objects
+ sSimmobjectLoaded sSimmobjectLoadedState;
+
+ //! cached sim objects and liveries read frim simconnect
+ std::vector vSimObjectsAndLiveries;
+
+ //! Simulator info
+ swift::misc::simulation::CSimulatorInfo m_simulatorInfo;
+
+ public:
+ //! @{
+ //! Offsets
+ static DWORD offsetSimObjAircraft(CSimConnectDefinitions::SimObjectRequest req)
+ {
+ return MaxSimObjAircraft * static_cast(req);
+ }
+ static DWORD offsetSimObjTerrainProbe(CSimConnectDefinitions::SimObjectRequest req)
+ {
+ return MaxSimObjProbes * static_cast(req);
+ }
+ //! @}
+ };
+
+ //! Listener for MSFS2024
+ class MSFS2024_EXPORT CSimulatorMsfs2024Listener : public swift::core::ISimulatorListener
+ {
+ Q_OBJECT
+
+ public:
+ //! Constructor
+ CSimulatorMsfs2024Listener(const swift::misc::simulation::CSimulatorPluginInfo &info);
+
+ //! \copydoc swift::core::ISimulatorListener::backendInfo
+ virtual QString backendInfo() const override;
+
+ protected:
+ //! \copydoc swift::core::ISimulatorListener::startImpl
+ virtual void startImpl() override;
+
+ //! \copydoc swift::core::ISimulatorListener::stopImpl
+ virtual void stopImpl() override;
+
+ //! \copydoc swift::core::ISimulatorListener::checkImpl
+ virtual void checkImpl() override;
+
+ private:
+ //! Test if connection can be established
+ void checkConnection();
+
+ //! Check simulator version and type
+ bool checkVersionAndSimulator() const;
+
+ //! Check the simconnect.dll
+ bool checkSimConnectDll() const;
+
+ //! Connect to simulator (if not already)
+ bool connectToSimulator();
+
+ //! Disconnect from simulator
+ bool disconnectFromSimulator();
+
+ //! Adjust the timer interval
+ void adjustTimerInterval(qint64 checkTimeMs);
+
+ static constexpr int MinQueryIntervalMs = 5 * 1000; // 5 seconds
+
+ QTimer m_timer { this }; //!< timer, "this" is needed otherwise I get warnings when move to new thread
+ QString m_simulatorVersion;
+ QString m_simConnectVersion;
+ QString m_simulatorName;
+ QString m_simulatorDetails;
+ HANDLE m_hSimConnect;
+ bool m_simConnected = false; //!< SimConnect is connected, does not mean to the correct sim.
+ swift::misc::CStatusMessage m_lastMessage; //!< last listener message
+
+ //! SimConnect Callback (simplified version for listener)
+ //! \sa CSimConnectObjects::SimConnectProc
+ static void CALLBACK SimConnectProc(SIMCONNECT_RECV *pData, DWORD cbData, void *pContext);
+ };
+} // namespace swift::simplugin::msfs2024common
+
+#endif // SWIFT_SIMPLUGIN_MSFS2024COMMON_SIMULATORMSFS2024COMMON_H
diff --git a/src/plugins/simulator/msfs2024/simulatormsfs2024simconnectproc.cpp b/src/plugins/simulator/msfs2024/simulatormsfs2024simconnectproc.cpp
new file mode 100644
index 000000000..4517b2d03
--- /dev/null
+++ b/src/plugins/simulator/msfs2024/simulatormsfs2024simconnectproc.cpp
@@ -0,0 +1,464 @@
+// SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors
+// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
+
+#include
+
+#include
+
+#include "simconnectdatadefinitionmsfs2024.h"
+#include "simulatormsfs2024common.h"
+
+#include "config/buildconfig.h"
+#include "core/application.h"
+#include "core/simulator.h"
+#include "misc/aviation/airportlist.h"
+#include "misc/logmessage.h"
+#include "misc/simulation/fscommon/bcdconversions.h"
+#include "misc/simulation/fsx/simconnectutilities.h"
+#include "misc/simulation/settings/simulatorsettings.h"
+#include "misc/simulation/simulatorplugininfo.h"
+
+using namespace swift::core;
+using namespace swift::config;
+using namespace swift::misc;
+using namespace swift::misc::simulation;
+using namespace swift::misc::aviation;
+using namespace swift::misc::physical_quantities;
+using namespace swift::misc::geo;
+using namespace swift::misc::network;
+using namespace swift::misc::simulation;
+using namespace swift::misc::simulation::fscommon;
+using namespace swift::misc::simulation::fsx;
+using namespace swift::misc::simulation::settings;
+
+namespace swift::simplugin::msfs2024common
+{
+ void CALLBACK CSimulatorMsfs2024::SimConnectProc(SIMCONNECT_RECV *pData, DWORD cbData, void *pContext)
+ {
+ // IMPORTANT:
+ // all tasks called in this function (ie all called functions) must perform fast or shall be called
+ // asynchronously
+
+ const qint64 procTimeStart = QDateTime::currentMSecsSinceEpoch();
+ CSimulatorMsfs2024 *simulatorMsfs2024 = static_cast(pContext);
+ const SIMCONNECT_RECV_ID recvId = static_cast(pData->dwID);
+ static const DataDefinitionOwnAircraftModel *dataDefinitionModel;
+ simulatorMsfs2024->m_dispatchReceiveIdLast = recvId;
+ simulatorMsfs2024->m_dispatchProcCount++;
+ const CSpecializedSimulatorSettings settings = simulatorMsfs2024->getSimulatorSettings();
+ CSimulatorSettings m_generic = settings.getGenericSettings();
+
+ switch (recvId)
+ {
+ case SIMCONNECT_RECV_ID_OPEN:
+ {
+ SIMCONNECT_RECV_OPEN *event = static_cast(pData);
+ const QString simConnectVersion = QStringLiteral("%1.%2.%3.%4")
+ .arg(event->dwSimConnectVersionMajor)
+ .arg(event->dwSimConnectVersionMinor)
+ .arg(event->dwSimConnectBuildMajor)
+ .arg(event->dwSimConnectBuildMinor);
+ const QString version = QStringLiteral("%1.%2.%3.%4")
+ .arg(event->dwApplicationVersionMajor)
+ .arg(event->dwApplicationVersionMinor)
+ .arg(event->dwApplicationBuildMajor)
+ .arg(event->dwApplicationBuildMinor);
+ const QString name = CSimulatorMsfs2024::fsxCharToQString(event->szApplicationName);
+ const QString details =
+ QStringLiteral("Name: '%1' Version: %2 SimConnect: %3").arg(name, version, simConnectVersion);
+ simulatorMsfs2024->setSimulatorDetails(name, details, version);
+ simulatorMsfs2024->m_simConnectVersion = simConnectVersion;
+ CLogMessage(simulatorMsfs2024).info(u"Connected to %1: '%2'")
+ << simulatorMsfs2024->getSimulatorPluginInfo().getIdentifier() << details;
+ simulatorMsfs2024->setSimConnected();
+ break; // SIMCONNECT_RECV_ID_OPEN
+ }
+ case SIMCONNECT_RECV_ID_EXCEPTION:
+ {
+ if (!simulatorMsfs2024->stillDisplayReceiveExceptions()) { break; }
+ simulatorMsfs2024->triggerAutoTraceSendId();
+
+ SIMCONNECT_RECV_EXCEPTION *exception = static_cast(pData);
+ const DWORD exceptionId = exception->dwException;
+ const DWORD sendId = exception->dwSendID;
+ const DWORD index = exception->dwIndex; // index of parameter that was source of error,
+ // 4294967295/0xFFFFFFFF means unknown, 0 means also UNKNOWN INDEX
+ const DWORD data = cbData;
+ const TraceFsxSendId trace = simulatorMsfs2024->getSendIdTrace(sendId);
+ bool logGenericExceptionInfo = true;
+
+ switch (exceptionId)
+ {
+ case SIMCONNECT_EXCEPTION_OPERATION_INVALID_FOR_OBJECT_TYPE: break;
+ case SIMCONNECT_EXCEPTION_UNRECOGNIZED_ID:
+ break; // Specifies that the client event, request ID, data definition ID, or object ID was not
+ // recognized
+ case SIMCONNECT_EXCEPTION_CREATE_OBJECT_FAILED:
+ {
+ if (trace.isValid())
+ {
+ // it can happen the object is not yet existing
+ CSimConnectObject simObject = simulatorMsfs2024->getSimObjectForTrace(trace);
+ if (simObject.isInvalid()) { simObject = trace.simObject; } // take the one in the trace
+ if (simObject.isValid())
+ {
+ if (simObject.isAircraft())
+ {
+ simulatorMsfs2024->addingAircraftFailed(simObject);
+ logGenericExceptionInfo = false;
+ }
+ else
+ {
+ const bool removed = simulatorMsfs2024->m_simConnectObjects.remove(simObject.getCallsign());
+ Q_UNUSED(removed);
+ CLogMessage(simulatorMsfs2024).warning(u"Adding probe failed: %1 %2")
+ << simObject.getCallsign().asString() << simObject.getAircraftModelString();
+ if (simulatorMsfs2024->isUsingFsxTerrainProbe())
+ {
+ CLogMessage(simulatorMsfs2024).warning(u"Disabling terrain probe");
+ simulatorMsfs2024->setUsingFsxTerrainProbe(false);
+ }
+ logGenericExceptionInfo = false;
+ } // aircraft
+ } // valid
+ } // trace
+ } // SIMCONNECT_EXCEPTION_CREATE_OBJECT_FAILED:
+ break;
+ default: break;
+ } // switch exception id
+
+ // generic exception warning
+ if (logGenericExceptionInfo)
+ {
+ QString ex = QString::asprintf("Exception=%lu | SendID=%lu | Index=%lu | cbData=%lu", exceptionId,
+ sendId, index, data);
+ const QString exceptionString(
+ CSimConnectUtilities::simConnectExceptionToString(static_cast(exception->dwException)));
+ const QString sendIdDetails = simulatorMsfs2024->getSendIdTraceDetails(sendId);
+ CLogMessage(simulatorMsfs2024).warning(u"Caught simConnect exception: '%1' '%2' | send details: '%3'")
+ << exceptionString << ex << (sendIdDetails.isEmpty() ? "N/A" : sendIdDetails);
+ }
+ break; // SIMCONNECT_RECV_ID_EXCEPTION
+ }
+ case SIMCONNECT_RECV_ID_QUIT:
+ {
+ simulatorMsfs2024->onSimExit();
+ break;
+ }
+ case SIMCONNECT_RECV_ID_EVENT:
+ {
+ const SIMCONNECT_RECV_EVENT *event = static_cast(pData);
+ switch (event->uEventID)
+ {
+ case SystemEventSimStatus:
+ {
+ const bool running = event->dwData ? true : false;
+ if (running) { simulatorMsfs2024->onSimRunning(); }
+ else { simulatorMsfs2024->onSimStopped(); }
+
+ // If the simulation stops, the model will be reloaded when it is restarted.
+ dataDefinitionModel = NULL;
+
+ break;
+ }
+ case SystemEventPause:
+ {
+ const bool paused = event->dwData ? true : false;
+ if (simulatorMsfs2024->m_simPaused != paused)
+ {
+ simulatorMsfs2024->m_simPaused = paused;
+ simulatorMsfs2024->emitSimulatorCombinedStatus();
+ }
+ break;
+ }
+ default: break;
+ }
+ break; // SIMCONNECT_RECV_ID_EVENT
+ }
+ case SIMCONNECT_RECV_ID_EVENT_OBJECT_ADDREMOVE:
+ {
+ const SIMCONNECT_RECV_EVENT_OBJECT_ADDREMOVE *event =
+ static_cast(pData);
+ const DWORD objectId = event->dwData;
+ const SIMCONNECT_SIMOBJECT_TYPE objectType = event->eObjType;
+ if (objectType != SIMCONNECT_SIMOBJECT_TYPE_AIRCRAFT && objectType != SIMCONNECT_SIMOBJECT_TYPE_HELICOPTER)
+ {
+ break; // SIMCONNECT_RECV_ID_EVENT_OBJECT_ADDREMOVE
+ }
+
+ // such an object is not necessarily one of ours
+ // for instance, I always see object "5" when I start the simulator
+ if (simulatorMsfs2024->getSimConnectObjects().isKnownSimObjectId(objectId))
+ {
+ switch (event->uEventID)
+ {
+ case SystemEventObjectRemoved: simulatorMsfs2024->simulatorReportedObjectRemoved(objectId); break;
+ case SystemEventObjectAdded:
+ // added in SIMCONNECT_RECV_ID_ASSIGNED_OBJECT_ID
+ // this event here is normally received before SIMCONNECT_RECV_ID_ASSIGNED_OBJECT_ID
+ break;
+ default: break;
+ }
+ }
+ break; // SIMCONNECT_RECV_ID_EVENT_OBJECT_ADDREMOVE
+ }
+ case SIMCONNECT_RECV_ID_EVENT_FRAME:
+ {
+ const SIMCONNECT_RECV_EVENT_FRAME *event = static_cast(pData);
+ switch (event->uEventID)
+ {
+ case SystemEventFrame:
+ // doing interpolation
+ simulatorMsfs2024->onSimFrame();
+ break;
+ default: break;
+ }
+ break; // SIMCONNECT_RECV_ID_EVENT_FRAME
+ }
+ case SIMCONNECT_RECV_ID_ASSIGNED_OBJECT_ID:
+ {
+ const SIMCONNECT_RECV_ASSIGNED_OBJECT_ID *event = static_cast(pData);
+ const DWORD requestId = event->dwRequestID;
+ const DWORD objectId = event->dwObjectID;
+
+ const QString Test = CSimulatorMsfs2024::requestIdToString(requestId);
+
+ simulatorMsfs2024->m_dispatchRequestIdLast = requestId;
+
+ if (CSimulatorMsfs2024::isRequestForSimConnectObject(requestId))
+ {
+ bool success = simulatorMsfs2024->setSimConnectObjectId(requestId, objectId);
+ if (!success) { break; } // not an request ID of ours
+ success = simulatorMsfs2024->simulatorReportedObjectAdded(
+ objectId); // adding failed (no IDs), trigger follow up actions
+ if (!success)
+ {
+ // getting here would mean object was removed in the meantime
+ // otherwise we will detect it in verification
+ const CSimConnectObject simObject = simulatorMsfs2024->getSimObjectForObjectId(objectId);
+ const CSimulatedAircraft remoteAircraft(simObject.getAircraft());
+ const CStatusMessage msg =
+ CStatusMessage(simulatorMsfs2024).error(u"Cannot add object %1, cs: '%2' model: '%3'")
+ << objectId << remoteAircraft.getCallsignAsString() << remoteAircraft.getModelString();
+ CLogMessage::preformatted(msg);
+ emit simulatorMsfs2024->physicallyAddingRemoteModelFailed(remoteAircraft, false, false, msg);
+ }
+ }
+ break; // SIMCONNECT_RECV_ID_ASSIGNED_OBJECT_ID
+ }
+ case SIMCONNECT_RECV_ID_SIMOBJECT_DATA_BYTYPE:
+ {
+ const SIMCONNECT_RECV_SIMOBJECT_DATA_BYTYPE *pObjData =
+ static_cast(pData);
+ const DWORD requestId = pObjData->dwRequestID;
+
+ switch (pObjData->dwRequestID)
+ {
+
+ case 0:
+ {
+ break;
+ }
+ case 1:
+ {
+ dataDefinitionModel = reinterpret_cast(&pObjData->dwData);
+ if (dataDefinitionModel->title != NULL && dataDefinitionModel->livery != NULL)
+ {
+ CAircraftModel model(dataDefinitionModel->title, dataDefinitionModel->livery,
+ CAircraftModel::TypeOwnSimulatorModel);
+
+ simulatorMsfs2024->reverseLookupAndUpdateOwnAircraftModel(model.getMsfs2024Modelstring());
+ }
+ break;
+ }
+
+ default: printf("Unhandled SIMCONNECT_RECV_ID_SIMOBJECT_DATA_BYTYPE received: %d\n", requestId); break;
+ }
+ break;
+
+ break;
+ }
+ case SIMCONNECT_RECV_ID_SIMOBJECT_DATA:
+ {
+ const SIMCONNECT_RECV_SIMOBJECT_DATA *pObjData = static_cast(pData);
+ const DWORD requestId = pObjData->dwRequestID;
+ simulatorMsfs2024->m_dispatchRequestIdLast = requestId;
+
+ switch (requestId)
+ {
+ case CSimConnectDefinitions::RequestOwnAircraft:
+ {
+ static_assert(sizeof(DataDefinitionOwnAircraft) == 45 * sizeof(double),
+ "DataDefinitionOwnAircraft has an incorrect size.");
+ const DataDefinitionOwnAircraft *ownAircaft =
+ reinterpret_cast(&pObjData->dwData);
+ simulatorMsfs2024->updateOwnAircraftFromSimulator(*ownAircaft);
+ break;
+ }
+ case CSimConnectDefinitions::RequestMSFSTransponder:
+ {
+ const DataDefinitionMSFSTransponderMode *transponderMode =
+ reinterpret_cast(&pObjData->dwData);
+ simulatorMsfs2024->updateMSFSTransponderMode(*transponderMode);
+ break;
+ }
+ default:
+ {
+ const DWORD objectId = pObjData->dwObjectID;
+ if (CSimulatorMsfs2024::isRequestForSimObjAircraft(requestId))
+ {
+ const CSimConnectObject simObject = simulatorMsfs2024->getSimObjectForObjectId(objectId);
+ if (!simObject.hasValidRequestAndObjectId()) { break; }
+ const CSimConnectDefinitions::SimObjectRequest subRequest =
+ CSimulatorMsfs2024::requestToSimObjectRequest(requestId);
+
+ if (subRequest == CSimConnectDefinitions::SimObjectPositionData)
+ {
+ static_assert(sizeof(DataDefinitionPosData) == 5 * sizeof(double),
+ "DataDefinitionPosData has an incorrect size.");
+ const DataDefinitionPosData *remoteAircraftSimData =
+ reinterpret_cast(&pObjData->dwData);
+ // extra check, but ids should be the same
+ if (objectId == simObject.getObjectId())
+ {
+ simulatorMsfs2024->triggerUpdateRemoteAircraftFromSimulator(simObject,
+ *remoteAircraftSimData);
+ }
+ } // position
+ else if (subRequest == CSimConnectDefinitions::SimObjectModel)
+ {
+ static_assert(sizeof(DataDefinitionRemoteAircraftModel) == sizeof(double) + 168 + 256,
+ "DataDefinitionRemoteAircraftModel has an incorrect size.");
+ const DataDefinitionRemoteAircraftModel *remoteAircraftModel =
+ reinterpret_cast(&pObjData->dwData);
+ // extra check, but ids should be the same
+ if (objectId == simObject.getObjectId())
+ {
+ simulatorMsfs2024->triggerUpdateRemoteAircraftFromSimulator(simObject,
+ *remoteAircraftModel);
+ }
+ } // model
+ else if (subRequest == CSimConnectDefinitions::SimObjectLights)
+ {
+ static_assert(sizeof(DataDefinitionRemoteAircraftLights) == 8 * sizeof(double),
+ "DataDefinitionRemoteAircraftLights has an incorrect size.");
+ const DataDefinitionRemoteAircraftLights *remoteAircraftLights =
+ reinterpret_cast(&pObjData->dwData);
+ // extra check, but ids should be the same
+ if (objectId == simObject.getObjectId())
+ {
+ const CCallsign callsign(simObject.getCallsign());
+ const CAircraftLights lights(remoteAircraftLights->toLights()); // as in simulator
+ simulatorMsfs2024->setCurrentLights(callsign, lights);
+ if (simObject.getLightsAsSent().isNull())
+ {
+ // allows to compare for toggle
+ simulatorMsfs2024->setLightsAsSent(callsign, lights);
+ }
+ }
+ break;
+ } // lights
+ else
+ {
+ if (CBuildConfig::isLocalDeveloperDebugBuild())
+ {
+ CLogMessage(simulatorMsfs2024).error(u"Unknown subrequest (aircraft): '%1' %2")
+ << CSimConnectDefinitions::simObjectRequestToString(subRequest)
+ << simObject.toQString();
+ }
+ }
+ }
+ else if (CSimulatorMsfs2024::isRequestForSimObjTerrainProbe(requestId))
+ {
+ const CSimConnectObject probeObj = simulatorMsfs2024->getSimObjectForObjectId(objectId);
+ if (!probeObj.hasValidRequestAndObjectId()) { break; }
+ Q_ASSERT_X(probeObj.isTerrainProbe(), Q_FUNC_INFO, "No probe");
+ const CSimConnectDefinitions::SimObjectRequest subRequest =
+ CSimulatorMsfs2024::requestToSimObjectRequest(requestId);
+
+ if (subRequest == CSimConnectDefinitions::SimObjectPositionData)
+ {
+ static_assert(sizeof(DataDefinitionPosData) == 5 * sizeof(double),
+ "DataDefinitionRemoteAircraftSimData has an incorrect size.");
+ const DataDefinitionPosData *probeSimData =
+ reinterpret_cast(&pObjData->dwData);
+ // extra check, but ids should be the same
+ if (objectId == probeObj.getObjectId())
+ {
+ const CCallsign cs = simulatorMsfs2024->m_pendingProbeRequests.value(requestId);
+ if (cs.isEmpty()) { break; }
+ simulatorMsfs2024->updateProbeFromSimulator(cs, *probeSimData);
+ }
+ }
+ else
+ {
+ if (CBuildConfig::isLocalDeveloperDebugBuild())
+ {
+ CLogMessage(simulatorMsfs2024).error(u"Unknown subrequest (probe): '%1' %2")
+ << CSimConnectDefinitions::simObjectRequestToString(subRequest) << probeObj.toQString();
+ }
+ }
+ } // probe
+ }
+ break; // default (SIMCONNECT_RECV_ID_SIMOBJECT_DATA)
+ }
+ break; // SIMCONNECT_RECV_ID_SIMOBJECT_DATA
+ }
+ case SIMCONNECT_RECV_ID_ENUMERATE_SIMOBJECT_AND_LIVERY_LIST: // 38
+ {
+ if (m_generic.getPropertyModelSet())
+ {
+ SIMCONNECT_RECV_ENUMERATE_SIMOBJECT_AND_LIVERY_LIST *msg =
+ (SIMCONNECT_RECV_ENUMERATE_SIMOBJECT_AND_LIVERY_LIST *)pData;
+ switch (msg->dwRequestID)
+ {
+ // case CSimConnectDefinitions::REQUEST_ALL:
+ // case CSimConnectDefinitions::REQUEST_USER:
+ // case CSimConnectDefinitions::REQUEST_BOAT:
+ // case CSimConnectDefinitions::REQUEST_GROUND:
+ // case CSimConnectDefinitions::REQUEST_ANIMAL:
+ case CSimConnectDefinitions::REQUEST_AIRPLANE:
+ case CSimConnectDefinitions::REQUEST_HELICOPTER:
+ case CSimConnectDefinitions::REQUEST_HOT_AIR: simulatorMsfs2024->CacheSimObjectAndLiveries(msg); break;
+ }
+ }
+
+ break;
+ }
+ case SIMCONNECT_RECV_ID_CLIENT_DATA:
+ {
+ if (!simulatorMsfs2024->m_useSbOffsets) { break; }
+ simulatorMsfs2024->m_sbDataReceived++;
+ const SIMCONNECT_RECV_CLIENT_DATA *clientData = static_cast(pData);
+ if (clientData->dwRequestID == CSimConnectDefinitions::RequestSbData)
+ {
+ //! \fixme FSUIPC vs SimConnect why is offset 19 ident 2/0? In FSUIPC it is 0/1, according to
+ //! documentation it is 0/1 but I receive 2/0 here. Whoever knows, add comment or fix if wrong
+ DataDefinitionClientAreaSb sbData;
+ std::memcpy(&sbData.data, &clientData->dwData, 128);
+ simulatorMsfs2024->updateOwnAircraftFromSimulator(sbData);
+ }
+ break; // SIMCONNECT_RECV_ID_CLIENT_DATA
+ }
+ case SIMCONNECT_RECV_ID_EVENT_FILENAME:
+ {
+ const SIMCONNECT_RECV_EVENT_FILENAME *event = static_cast(pData);
+ switch (event->uEventID)
+ {
+ case SystemEventFlightLoaded: break;
+ default: break;
+ }
+ break; // SIMCONNECT_RECV_ID_EVENT_FILENAME
+ }
+ default: simulatorMsfs2024->m_dispatchProcEmptyCount++; break;
+ } // main switch
+
+ // performance stats
+ const qint64 procTimeEnd = QDateTime::currentMSecsSinceEpoch();
+ simulatorMsfs2024->m_dispatchProcTimeMs = procTimeEnd - procTimeStart;
+ if (simulatorMsfs2024->m_dispatchProcTimeMs > simulatorMsfs2024->m_dispatchProcMaxTimeMs)
+ {
+ simulatorMsfs2024->m_dispatchProcMaxTimeMs = simulatorMsfs2024->m_dispatchProcTimeMs;
+ }
+ } // method
+} // namespace swift::simplugin::msfs2024common
diff --git a/src/swiftdata/CMakeLists.txt b/src/swiftdata/CMakeLists.txt
index 1dc6536e9..22ccfd48f 100644
--- a/src/swiftdata/CMakeLists.txt
+++ b/src/swiftdata/CMakeLists.txt
@@ -7,6 +7,8 @@ add_executable(swiftdata WIN32
swiftdata.h
swiftdata.ui
swiftdatamenus.cpp
+ swiftdataapplication.cpp
+ swiftdataapplication.h
)
if(WIN32)
diff --git a/src/swiftdata/main.cpp b/src/swiftdata/main.cpp
index e8d80900a..2b01ba6e2 100644
--- a/src/swiftdata/main.cpp
+++ b/src/swiftdata/main.cpp
@@ -11,6 +11,7 @@
#include "misc/crashhandler.h"
#include "misc/icons.h"
#include "swiftdata.h"
+#include "swiftdataapplication.h"
using namespace swift::misc;
using namespace swift::core;
@@ -23,19 +24,25 @@ int main(int argc, char *argv[])
Q_UNUSED(qa)
CCrashHandler::instance()->init();
- CGuiApplication a(CApplicationInfo::swiftMappingTool(), CApplicationInfo::MappingTool, CIcons::swiftDatabase48());
- if (!a.parseCommandLineArgsAndLoadSetup()) { return EXIT_FAILURE; }
- a.splashScreen(CIcons::swiftDatabase256());
- a.initAndStartWebDataServices(swift::core::CWebReaderFlags::AllSwiftDbReaders,
- CDatabaseReaderConfigList::forMappingTool());
- a.startCoreFacadeWithoutContexts();
- if (!a.start())
+
+ int r = 0;
{
- a.gracefulShutdown();
- return EXIT_FAILURE;
+ // CGuiApplication a(CApplicationInfo::swiftMappingTool(), CApplicationInfo::MappingTool,
+ // CIcons::swiftDatabase48());
+ CSwiftDataApplication a; // application with contexts
+ if (!a.parseCommandLineArgsAndLoadSetup()) { return EXIT_FAILURE; }
+ a.splashScreen(CIcons::swiftDatabase256());
+ a.initAndStartWebDataServices(swift::core::CWebReaderFlags::AllSwiftDbReaders,
+ CDatabaseReaderConfigList::forMappingTool());
+ a.startCoreFacadeWithoutContexts();
+ if (!a.start())
+ {
+ a.gracefulShutdown();
+ return EXIT_FAILURE;
+ }
+ CSwiftData w;
+ w.show();
+ int r = a.exec();
}
- CSwiftData w;
- w.show();
- int r = a.exec();
return r;
}
diff --git a/src/swiftdata/swiftdata.h b/src/swiftdata/swiftdata.h
index 4989e913d..602e1a779 100644
--- a/src/swiftdata/swiftdata.h
+++ b/src/swiftdata/swiftdata.h
@@ -25,7 +25,8 @@ namespace Ui
namespace swift::core
{
class CWebDataServices;
-}
+ // class CPluginManagerSimulator;
+} // namespace swift::core
namespace swift::gui::components
{
class CAutoPublishDialog;
diff --git a/src/swiftdata/swiftdataapplication.cpp b/src/swiftdata/swiftdataapplication.cpp
new file mode 100644
index 000000000..46d52bf9e
--- /dev/null
+++ b/src/swiftdata/swiftdataapplication.cpp
@@ -0,0 +1,106 @@
+// SPDX-FileCopyrightText: Copyright (C) 2016 swift Project Community / Contributors
+// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
+
+#include "swiftdataapplication.h"
+
+#include
+
+#include "core/application.h"
+#include "core/corefacadeconfig.h"
+#include "core/coremodeenums.h"
+#include "misc/dbusserver.h"
+#include "misc/icons.h"
+
+using namespace swift::misc;
+using namespace swift::core;
+
+CSwiftDataApplication::CSwiftDataApplication()
+ : CGuiApplication(CApplicationInfo::swiftMappingTool(), CApplicationInfo::MappingTool, CIcons::swiftDatabase48())
+{
+ // this->addParserOption(m_cmdFacadeMode);
+ // this->addDBusAddressOption();
+ // this->addNetworkOptions();
+ // this->addAudioOptions();
+}
+
+// TODO TZ remove commented code after testing
+CStatusMessageList CSwiftDataApplication::startHookIn()
+{
+ Q_ASSERT_X(m_parsed, Q_FUNC_INFO, "Not yet parsed cmd line arguments");
+
+ QString dBusAddress(this->getCmdDBusAddressValue());
+ const QString coreModeStr =
+ this->isParserOptionSet(m_cmdFacadeMode) ? this->getParserValue(m_cmdFacadeMode) : QString();
+ CoreModes::CoreMode coreMode = CoreModes::stringToCoreMode(coreModeStr);
+
+ //// Valid combination?
+ // if (!coreModeStr.isEmpty())
+ //{
+ // if (coreMode == CoreModes::Standalone && !dBusAddress.isEmpty())
+ // {
+ // const CStatusMessage m =
+ // CStatusMessage(this, CLogCategories::validation()).error(u"Inconsistent pair DBus: '%1' and core:
+ // '%2'")
+ // << dBusAddress << coreModeStr;
+ // return CStatusMessageList(m);
+ // }
+ // }
+
+ //// Implicit configuration
+ CStatusMessageList msgs;
+ // if (!dBusAddress.isEmpty() && coreModeStr.isEmpty())
+ //{
+ // coreMode = CoreModes::Distributed; // default
+ // const CStatusMessage m =
+ // CStatusMessage(this, CLogCategories::validation()).info(u"No DBus address, setting core mode: '%1'")
+ // << CoreModes::coreModeToString(coreMode);
+ // msgs.push_back(m);
+ // }
+ // else if (dBusAddress.isEmpty() && coreMode == CoreModes::Distributed)
+ //{
+ // dBusAddress = CDBusServer::sessionBusAddress(); // a possible default
+ // const CStatusMessage m =
+ // CStatusMessage(this, CLogCategories::validation()).info(u"Setting DBus address to '%1'") << dBusAddress;
+ // msgs.push_back(m);
+ // }
+
+ CCoreFacadeConfig runtimeConfig = coreModeToCoreFacadeConfig(coreMode, dBusAddress);
+ const CStatusMessageList contextMsgs = this->initContextsAndStartCoreFacade(runtimeConfig);
+ // const CStatusMessageList contextMsgs = { CStatusMessage(this, CLogCategories::validation()).info(u"TEST") };
+ msgs.push_back(contextMsgs);
+ return contextMsgs;
+}
+
+bool CSwiftDataApplication::parsingHookIn()
+{
+ // Parse core relevant arguments
+ const QString dBusAddress(this->getCmdDBusAddressValue());
+ // if (!dBusAddress.isEmpty())
+ //{
+ // // check if reachable
+ // if (!CDBusServer::isDBusAvailable(dBusAddress))
+ // {
+ // this->cmdLineErrorMessage("DBus error", "DBus server at '" + dBusAddress + "' can not be reached");
+ // return false;
+ // }
+ // }
+ return CGuiApplication::parsingHookIn();
+}
+
+CSwiftDataApplication *CSwiftDataApplication::instance()
+{
+ return qobject_cast(CApplication::instance());
+}
+
+CCoreFacadeConfig CSwiftDataApplication::coreModeToCoreFacadeConfig(CoreModes::CoreMode coreMode,
+ const QString &dBusAddress)
+{
+ switch (coreMode)
+ {
+ case CoreModes::Distributed: return CCoreFacadeConfig(CCoreFacadeConfig::Remote, dBusAddress);
+ case CoreModes::Standalone: return CCoreFacadeConfig(CCoreFacadeConfig::Local, dBusAddress); break;
+ default:
+ Q_ASSERT_X(false, Q_FUNC_INFO, "Not handled core mode");
+ return CCoreFacadeConfig(CCoreFacadeConfig::NotUsed, dBusAddress);
+ }
+}
diff --git a/src/swiftdata/swiftdataapplication.h b/src/swiftdata/swiftdataapplication.h
new file mode 100644
index 000000000..50c516862
--- /dev/null
+++ b/src/swiftdata/swiftdataapplication.h
@@ -0,0 +1,48 @@
+// SPDX-FileCopyrightText: Copyright (C) 2016 swift Project Community / Contributors
+// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
+
+//! \file
+
+#ifndef SWIFTDATAAPPLICATION_H
+#define SWIFTDATAAPPLICATION_H
+
+#include
+#include
+#include
+#include
+
+#include "core/coremodeenums.h"
+#include "gui/guiapplication.h"
+
+/*!
+ * Specialized GUI application for swift pilot client.
+ * Handles parsing of some specialized CMD line argumenets and startup of core
+ */
+class CSwiftDataApplication : public swift::gui::CGuiApplication
+{
+ Q_OBJECT
+
+public:
+ //! Constructor
+ CSwiftDataApplication();
+
+ //! Single instance
+ CSwiftDataApplication *instance();
+
+protected:
+ //! Parsing of special CMD args
+ virtual bool parsingHookIn() override;
+
+ //! Start facade by cmd arguments
+ virtual swift::misc::CStatusMessageList startHookIn() override;
+
+private:
+ static swift::core::CCoreFacadeConfig coreModeToCoreFacadeConfig(swift::core::CoreModes::CoreMode,
+ const QString &dBusAddress);
+
+ QCommandLineOption m_cmdFacadeMode { { "c", "core" },
+ QCoreApplication::translate("main", "Core mode: (d)istributed, (s)tandalone."),
+ "coremode" }; //!< Facade startup mode
+};
+
+#endif // SWIFTDATAAPPLICATION_H