31#include <simgear/debug/logstream.hxx>
32#include <simgear/misc/sg_path.hxx>
33#include <simgear/misc/strutils.hxx>
34#include <simgear/props/props.hxx>
35#include <simgear/props/props_io.hxx>
36#include <simgear/structure/exception.hxx>
48namespace strutils = simgear::strutils;
68const unique_ptr<AddonManager>&
71 SG_LOG(SG_GENERAL, SG_DEBUG,
"Initializing the AddonManager");
78const unique_ptr<AddonManager>&
88 SG_LOG(SG_GENERAL, SG_DEBUG,
"Resetting the AddonManager");
94AddonManager::loadConfigFileIfExists(
const SGPath& configFile)
96 if (!configFile.exists()) {
100 SGPropertyNode_ptr configProps(
new SGPropertyNode);
102 readProperties(configFile, configProps);
103 }
catch (
const sg_exception &e) {
104 throw errors::error_loading_config_file(
105 "unable to load add-on config file '" + configFile.utf8Str() +
"': " +
106 e.getFormattedMessage());
114 copyPropertiesIf(configProps,
globals->
get_props(), [](
const SGPropertyNode* src) {
115 if (src->nChildren() > 0)
119 auto dstNode = globals->get_props()->getNode(src->getPath());
125 return dstNode->getAttribute(SGPropertyNode::USERARCHIVE) == false;
128 SG_LOG(SG_GENERAL, SG_INFO,
129 "Loaded add-on config file: '" << configFile.utf8Str() +
"'");
133AddonManager::registerAddonMetadata(
const SGPath& addonPath)
135 using ptr_traits = shared_ptr_traits<AddonRef>;
137 string addonId = addon->getId();
139 SGPropertyNode* addonPropNode =
fgGetNode(
"addons",
true)
140 ->getChild(
"by-id", 0, 1)
141 ->getChild(addonId, 0, 1);
142 addon->setAddonNode(addonPropNode);
143 addon->setLoadSequenceNumber(_loadSequenceNumber++);
146 std::string minFGversion = addon->getMinFGVersionRequired();
147 if (strutils::compare_versions(FLIGHTGEAR_VERSION, minFGversion) < 0) {
148 throw errors::fg_version_too_old(
149 "add-on '" + addonId +
"' requires FlightGear " + minFGversion +
150 " or later, however this is FlightGear " + FLIGHTGEAR_VERSION);
153 std::string maxFGversion = addon->getMaxFGVersionRequired();
154 if (maxFGversion !=
"none" &&
155 strutils::compare_versions(FLIGHTGEAR_VERSION, maxFGversion) > 0) {
156 throw errors::fg_version_too_recent(
157 "add-on '" + addonId +
"' requires FlightGear " + maxFGversion +
158 " or earlier, however this is FlightGear " + FLIGHTGEAR_VERSION);
162 auto emplaceRetval = _idToAddonMap.emplace(addonId, std::move(addon));
165 if (!emplaceRetval.second) {
166 auto existingElt = _idToAddonMap.find(addonId);
167 assert(existingElt != _idToAddonMap.end());
168 throw errors::duplicate_registration_attempt(
169 "attempt to register add-on '" + addonId +
"' with base path '"
170 + addonPath.utf8Str() +
"', however it is already registered with base "
171 "path '" + existingElt->second->getBasePath().utf8Str() +
"'");
183 const SGPath addonRealPath = addonPath.realpath();
184 const string addonId = registerAddonMetadata(addonRealPath);
185 loadConfigFileIfExists(addonRealPath /
"addon-config.xml");
186 globals->append_aircraft_path(addonRealPath);
189 addon->getLoadedFlagNode()->setBoolValue(
false);
190 SGPropertyNode_ptr
addonNode = addon->getAddonNode();
193 addonNode->getNode(
"id",
true)->setStringValue(addonId);
194 addonNode->getNode(
"name",
true)->setStringValue(addon->getName());
197 addonNode->getNode(
"path",
true)->setStringValue(addonRealPath.utf8Str());
199 ->setIntValue(addon->getLoadSequenceNumber());
202 SGPropertyNode* seqNumNode =
fgGetNode(
"addons",
true)->addChild(
"addon");
203 seqNumNode->getNode(
"path",
true)->setStringValue(addonRealPath.utf8Str());
205 string msg =
"Registered add-on '" + addon->getName() +
"' (" + addonId +
207 "base path is '" + addonRealPath.utf8Str() +
"'";
209 auto dataPath = addonRealPath /
"FGData";
210 if (dataPath.exists()) {
211 SG_LOG(SG_GENERAL, SG_INFO,
"Registering data path for add-on: " << addon->getName());
212 globals->append_data_path(dataPath,
true );
216 _registeredAddons.push_back(addon);
217 SG_LOG(SG_GENERAL, SG_INFO, msg);
225 return (_idToAddonMap.find(addonId) != _idToAddonMap.end());
232 getAddon(addonId)->getLoadedFlagNode()->getBoolValue());
238 return _registeredAddons;
245 v.reserve(_idToAddonMap.size());
247 for (
const auto& elem: _idToAddonMap) {
249 v.push_back(elem.second);
259 const auto it = _idToAddonMap.find(addonId);
261 if (it == _idToAddonMap.end()) {
262 throw sg_exception(
"tried to get add-on '" + addonId +
"', however no "
263 "such add-on has been registered.");
272 return getAddon(addonId)->getVersion();
278 return getAddon(addonId)->getBasePath();
283 return getAddon(addonId)->getAddonNode();
289 for (
const auto& addon: _registeredAddons) {
290 addon->addToFGMenubar();
SGPropertyNode * get_props()
AddonRef getAddon(const std::string &addonId) const
std::vector< AddonRef > registeredAddons() const
static const std::unique_ptr< AddonManager > & createInstance()
SGPath addonBasePath(const std::string &addonId) const
SGPropertyNode_ptr addonNode(const std::string &addonId) const
AddonVersionRef addonVersion(const std::string &addonId) const
std::string registerAddon(const SGPath &addonPath)
AddonManager(const AddonManager &)=delete
void addAddonMenusToFGMenubar() const
bool isAddonLoaded(const std::string &addonId) const
static const std::unique_ptr< AddonManager > & instance()
bool isAddonRegistered(const std::string &addonId) const
std::vector< AddonRef > loadedAddons() const
static Addon fromAddonDir(const SGPath &addonPath)
SGSharedPtr< Addon > AddonRef
static unique_ptr< AddonManager > staticInstance
SGSharedPtr< AddonVersion > AddonVersionRef
FlightPlan.hxx - defines a full flight-plan object, including departure, cruise, arrival information ...
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.