27#include <osgViewer/Viewer>
28#include <osgDB/Registry>
30#include <simgear/structure/commands.hxx>
31#include <simgear/structure/exception.hxx>
32#include <simgear/misc/sg_path.hxx>
33#include <simgear/misc/sg_dir.hxx>
34#include <simgear/timing/sg_time.hxx>
35#include <simgear/ephemeris/ephemeris.hxx>
36#include <simgear/structure/subsystem_mgr.hxx>
37#include <simgear/structure/event_mgr.hxx>
39#include <simgear/misc/ResourceManager.hxx>
40#include <simgear/props/propertyObject.hxx>
41#include <simgear/props/props_io.hxx>
42#include <simgear/props/AtomicChangeListener.hxx>
43#include <simgear/scene/model/modellib.hxx>
44#include <simgear/package/Root.hxx>
61#include <simgear/sound/soundmgr.hxx>
62#include <simgear/scene/material/matlib.hxx>
78 virtual SGPath
resolve(
const std::string& aResource, SGPath&)
const
81 if ((pieces.size() < 3) || (pieces.front() !=
"Aircraft")) {
86 const std::string aircraftDir =
fgGetString(
"/sim/aircraft-dir");
87 string_list aircraftDirPieces(sgPathBranchSplit(aircraftDir));
88 if (!aircraftDirPieces.empty() && (aircraftDirPieces.back() == pieces[1])) {
90 SGPath r(aircraftDir);
91 for (
unsigned int i=2;
i<pieces.size(); ++
i) {
106 std::string res(aResource, 9);
108 PathList::const_iterator it = dirs.begin();
109 for (; it != dirs.end(); ++it) {
125 simgear::ResourceProvider(
simgear::ResourceManager::PRIORITY_HIGH)
129 virtual SGPath
resolve(
const std::string& aResource, SGPath&)
const
131 SGPath
p = SGPath::fromUtf8(
fgGetString(
"/sim/aircraft-dir"));
133 return p.exists() ?
p : SGPath();
148 subsystem_mgr( new SGSubsystemMgr ),
149 event_mgr( new SGEventMgr ),
154 commands( SGCommandMgr::instance() ),
155 channel_options_list( NULL ),
156 initial_waypoints( NULL ),
158 haveUserSettings(false)
160 SGPropertyNode* root =
new SGPropertyNode;
161 props = SGPropertyNode_ptr(root);
164 auto resMgr = simgear::ResourceManager::instance();
171void FGGlobals::initProperties()
173 simgear::PropertyObjectBase::setDefaultRoot(props);
175 positionLon = props->getNode(
"position/longitude-deg",
true);
176 positionLat = props->getNode(
"position/latitude-deg",
true);
177 positionAlt = props->getNode(
"position/altitude-ft",
true);
179 viewLon = props->getNode(
"sim/current-view/viewer-lon-deg",
true);
180 viewLat = props->getNode(
"sim/current-view/viewer-lat-deg",
true);
181 viewAlt = props->getNode(
"sim/current-view/viewer-elev-ft",
true);
183 referenceOffsetX = props->getNode(
"sim/model/reference-offset-x",
true);
184 referenceOffsetY = props->getNode(
"sim/model/reference-offset-y",
true);
185 referenceOffsetZ = props->getNode(
"sim/model/reference-offset-z",
true);
187 orientPitch = props->getNode(
"orientation/pitch-deg",
true);
188 orientHeading = props->getNode(
"orientation/heading-deg",
true);
189 orientRoll = props->getNode(
"orientation/roll-deg",
true);
203 osgViewer::ViewerBase* vb = renderer->getViewerBase();
213 subsystem_mgr->shutdown();
214 subsystem_mgr->unbind();
219 osgViewer::View* v = renderer->getView();
220 if (v && v->getDatabasePager()) {
221 v->getDatabasePager()->cancel();
222 v->getDatabasePager()->clear();
226 osgDB::Registry::instance()->clearObjectCache();
236 delete subsystem_mgr;
237 subsystem_mgr =
nullptr;
241 delete channel_options_list;
242 delete initial_waypoints;
252 simgear::PropertyObjectBase::setDefaultRoot(NULL);
253 simgear::SGModelLib::resetPropertyRoot();
261 delete simgear::ResourceManager::instance();
267 fg_root = tmp.realpath();
270 tmp.append(
"data" );
271 tmp.append(
"version" );
272 if ( tmp.exists() ) {
273 fgGetNode(
"BAD_FG_ROOT",
true)->setStringValue(fg_root.utf8Str());
274 fg_root.append(
"data");
275 fgGetNode(
"GOOD_FG_ROOT",
true)->setStringValue(fg_root.utf8Str());
276 SG_LOG(SG_GENERAL, SG_ALERT,
"***\n***\n*** Warning: changing bad FG_ROOT/--fg-root to '"
277 << fg_root <<
"'\n***\n***");
282 SGPropertyNode *n =
fgGetNode(
"/sim",
true);
283 n->removeChild(
"fg-root", 0);
284 n = n->getChild(
"fg-root", 0,
true);
285 n->setStringValue(fg_root.utf8Str());
286 n->setAttribute(SGPropertyNode::WRITE,
false);
288 simgear::ResourceManager::instance()->addBasePath(fg_root,
289 simgear::ResourceManager::PRIORITY_DEFAULT);
295 fg_home = home.realpath();
300 texture_cache_dir = textureCache.realpath();
301 auto node =
fgGetNode(
"/sim/rendering/texture-cache/dir",
true);
302 node->setAttribute(SGPropertyNode::WRITE,
true);
303 node->setStringValue(textureCache.utf8Str());
304 node->setAttribute(SGPropertyNode::WRITE,
false);
311 r.push_back(fg_root);
312 r.insert(r.end(), _dataPathsAfterFGRoot.begin(), _dataPathsAfterFGRoot.end());
331 if (!path.exists()) {
332 SG_LOG(SG_GENERAL, SG_WARN,
"adding non-existant data path:" << path);
335 using RM = simgear::ResourceManager;
336 auto resManager = RM::instance();
338 _dataPathsAfterFGRoot.push_back(path);
340 resManager->addBasePath(path,
static_cast<RM::Priority
>(RM::PRIORITY_DEFAULT - 10));
342 additional_data_paths.push_back(path);
344 resManager->addBasePath(path,
static_cast<RM::Priority
>(RM::PRIORITY_DEFAULT + 10));
350 for (SGPath
p : additional_data_paths) {
351 p.append(pathSuffix);
357 SGPath rootPath(fg_root);
358 rootPath.append(pathSuffix);
359 if (rootPath.exists()) {
363 for (SGPath
p : _dataPathsAfterFGRoot) {
364 p.append(pathSuffix);
375 for (
const SGPath& path : paths) {
382 SGPropertyNode* sim =
fgGetNode(
"/sim",
true);
386 while (sim->getChild(
"fg-scenery", propIndex) != NULL) {
390 SGPath abspath(path.realpath());
391 if (!abspath.exists()) {
392 SG_LOG(SG_GENERAL, SG_WARN,
"scenery path not found:" << abspath);
397 PathList::const_iterator ex = std::find(fg_scenery.begin(), fg_scenery.end(), abspath);
398 if (ex != fg_scenery.end()) {
399 SG_LOG(SG_GENERAL, SG_INFO,
"skipping duplicate add of scenery path:" << abspath);
405 simgear::ResourceManager::instance()->addBasePath(abspath, simgear::ResourceManager::PRIORITY_DEFAULT);
407 fg_scenery.push_back(abspath);
408 extra_read_allowed_paths.push_back(abspath);
411 SGPropertyNode* n = sim->getChild(
"fg-scenery", propIndex++,
true);
412 n->setStringValue(abspath.utf8Str());
413 n->setAttribute(SGPropertyNode::WRITE,
false);
416 n->setAttribute(SGPropertyNode::PRESERVE,
true);
419 SGPath sources = SGPath(abspath);
420 sources.append(
"sources.xml");
421 SG_LOG(SG_TERRAIN, SG_DEBUG,
"Looking for source file " << sources <<
". Exists ? " << sources.exists());
422 if (sources.exists()) {
424 SGPropertyNode* sourcesProp =
fgGetNode(
"/scenery/sources",
true);
426 while (sourcesProp->getChild(
"directory", propIndex) != NULL) {
430 sourcesProp = sourcesProp->getChild(
"directory", propIndex++,
true);
433 sourcesProp->setStringValue(
"path", abspath.utf8Str());
436 if(!
fgLoadProps(sources.utf8Str(), sourcesProp,
false))
438 SG_LOG(SG_TERRAIN, SG_ALERT,
"Unable to load sources file " << sources.utf8Str());
442 sourcesProp->setAttribute(SGPropertyNode::WRITE,
false);
443 sourcesProp->setAttribute(SGPropertyNode::PRESERVE,
true);
450 SGPath abspath(path.realpath());
451 if (!abspath.exists()) {
452 SG_LOG(SG_IO, SG_DEBUG,
"read-allowed path not found:" << abspath);
455 extra_read_allowed_paths.push_back(abspath);
461 fgGetNode(
"/sim",
true)->removeChildren(
"fg-scenery");
470 SGPath abspath(path.realpath());
471 download_dir = abspath;
479 SGPropertyNode *n =
fgGetNode(
"/sim/paths/download-dir",
true);
480 n->setAttribute(SGPropertyNode::WRITE,
true);
481 n->setStringValue(abspath.utf8Str());
482 n->setAttribute(SGPropertyNode::WRITE,
false);
491 if (terrasync_dir.realpath() != SGPath(
fgGetString(
"/sim/terrasync/scenery-dir")).realpath()) {
493 SG_LOG(SG_GENERAL, SG_WARN,
"/sim/terrasync/scenery-dir is no longer stored across runs: if you wish to keep using a non-standard Terrasync directory, use --terrasync-dir or the launcher's settings");
495 SGPath abspath(path.realpath());
496 terrasync_dir = abspath;
499 SGPropertyNode *n =
fgGetNode(
"/sim/terrasync/scenery-dir",
true);
500 n->setAttribute(SGPropertyNode::WRITE,
true);
501 n->setStringValue(abspath.utf8Str());
502 n->setAttribute(SGPropertyNode::WRITE,
false);
510 catalog_aircraft_dir = path;
516 if (!catalog_aircraft_dir.isNull()) {
517 r.push_back(catalog_aircraft_dir);
520 r.insert(r.end(), fg_aircraft_dirs.begin(), fg_aircraft_dirs.end());
526 SGPath dirPath(path);
527 if (!dirPath.exists()) {
528 SG_LOG(SG_GENERAL, SG_WARN,
"aircraft path not found:" << path);
532 SGPath acSubdir(dirPath);
533 acSubdir.append(
"Aircraft");
534 if (acSubdir.exists()) {
538 "Specified an aircraft-dir with an 'Aircraft' subdirectory:" << dirPath
539 <<
", will instead use child directory:" << acSubdir
544 fg_aircraft_dirs.push_back(dirPath.realpath());
545 extra_read_allowed_paths.push_back(dirPath.realpath());
550 for (
unsigned int p = 0;
p<paths.size(); ++
p) {
557 return simgear::ResourceManager::instance()->findPath(branch);
562 return simgear::ResourceManager::instance()->findPath(branch);
567 return simgear::ResourceManager::instance()
568 ->findPath(branch, SGPath(
fgGetString(
"/sim/aircraft-dir")));
579 if (render == renderer) {
591 return subsystem_mgr;
597 if (!subsystem_mgr) {
601 return subsystem_mgr->get_subsystem(
name);
613 return SGGeod::fromDegFt(positionLon->getDoubleValue(),
614 positionLat->getDoubleValue(),
615 positionAlt->getDoubleValue());
626 heading = orientHeading->getDoubleValue();
627 pitch = orientPitch->getDoubleValue();
628 roll = orientRoll->getDoubleValue();
634 return SGGeod::fromDegFt(viewLon->getDoubleValue(),
635 viewLat->getDoubleValue(),
636 viewAlt->getDoubleValue());
648 if (referenceOffsetX)
649 pos[0] += referenceOffsetX->getDoubleValue();
651 if (referenceOffsetY)
652 pos[1] += referenceOffsetY->getDoubleValue();
654 if (referenceOffsetZ)
655 pos[2] += referenceOffsetZ->getDoubleValue();
662 for (
int i=0;
i<nd->nChildren(); ++
i) {
663 SGPropertyNode* cp = nd->getChild(
i);
664 if (SGReferenced::count(cp) > 1) {
665 SG_LOG(SG_GENERAL, SG_INFO,
"\t" << cp->getPath() <<
" refcount:" << SGReferenced::count(cp));
678 for (
int i=0;
i<nd->nChildren(); ++
i) {
679 SGPropertyNode* cp = nd->getChild(
i);
690 terrasync_dir = SGPath{};
704 orientHeading.clear();
710 SG_LOG(SG_GENERAL, SG_INFO,
"root props refcount:" << props.getNumRefs());
715 props =
new SGPropertyNode;
720 SGPropertyNode *n = props->getNode(
"/sim",
true);
721 n->removeChild(
"fg-root", 0);
722 n = n->getChild(
"fg-root", 0,
true);
723 n->setStringValue(fg_root.utf8Str());
724 n->setAttribute(SGPropertyNode::WRITE,
false);
729 std::ostringstream os;
730 string_list versionParts = simgear::strutils::split(VERSION,
".");
731 if (versionParts.size() < 2) {
732 return "autosave.xml";
735 os <<
"autosave_" << versionParts[0] <<
"_" << versionParts[1] <<
".xml";
741 if (userDataPath.isNull()) {
750 const std::string path(props->getPath());
751 auto it = std::find_if(blacklist.begin(), blacklist.end(), [path](
const std::string& black)
752 { return simgear::strutils::matchPropPathToTemplate(path, black); });
753 if (it != blacklist.end()) {
754 SGPropertyNode* pr = props->getParent();
755 pr->removeChild(props);
760 for (
int c=0; c < props->nChildren(); ++c) {
770 if (a.first == b.first) {
771 return a.second < b.second;
774 return a.first < b.first;
779 const string_list versionParts = simgear::strutils::split(VERSION,
".");
780 if (versionParts.size() < 2) {
784 simgear::Dir userDataDir(userDataPath);
787 const VersionPair currentVersion(simgear::strutils::to_int(versionParts[0]),
788 simgear::strutils::to_int(versionParts[1]));
790 for (
auto previousSave : userDataDir.children(simgear::Dir::TYPE_FILE,
".xml")) {
791 const std::string base = previousSave.file_base();
794 const int matches = ::sscanf(base.c_str(),
"autosave_%d_%d", &v.first, &v.second);
799 if (currentVersion < v) {
805 if (v.first < 2000) {
811 if (foundVersion < v) {
813 migratePath = previousSave;
817 if (!migratePath.exists()) {
821 SG_LOG(SG_GENERAL, SG_INFO,
"Migrating old autosave:" << migratePath);
822 SGPropertyNode oldProps;
825 readProperties(migratePath, &oldProps, SGPropertyNode::USERARCHIVE);
826 }
catch (sg_exception& e) {
827 SG_LOG(SG_GENERAL, SG_WARN,
"failed to read previous user settings:" << e.getMessage()
828 <<
"(from " << e.getOrigin() <<
")");
834 SGPropertyNode_ptr blacklistNode =
fgGetNode(
"/sim/autosave-migration/blacklist",
true);
835 for (
auto node : blacklistNode->getChildren(
"path")) {
836 blacklist.push_back(node->getStringValue());
844 if (foundVersion.first < 2019) {
845 SGPropertyNode_ptr wheelNode = oldProps.getNode(
"/sim/mouse/invert-mouse-wheel");
847 wheelNode->setBoolValue(!wheelNode->getBoolValue());
851 copyProperties(&oldProps, props);
855 fgSetBool(
"/sim/autosave-migration/did-migrate",
true);
862 if (userDataPath.isNull()) {
867 haveUserSettings =
true;
870 SGPropertyNode autosave;
871 if (autosaveFile.exists()) {
872 SG_LOG(SG_INPUT, SG_INFO,
873 "Reading user settings from " << autosaveFile);
875 readProperties(autosaveFile, &autosave, SGPropertyNode::USERARCHIVE);
876 }
catch (sg_exception& e) {
877 SG_LOG(SG_INPUT, SG_WARN,
"failed to read user settings:" << e.getMessage()
878 <<
"(from " << e.getOrigin() <<
")");
886 SGPropertyNode* ai = autosave.getNode(
"ai");
888 ai->removeChildren(
"models");
890 copyProperties(&autosave,
globals->get_props());
904 if (userDataPath.isNull()) userDataPath =
get_fg_home();
908 if (!haveUserSettings)
911 if (
fgGetBool(
"/sim/startup/save-on-exit")) {
913 haveUserSettings =
false;
916 autosaveFile.create_dir( 0700 );
918 SGPath tmpFile = autosaveFile.dirPath() /
"autosave.tmp";
919 SG_LOG(SG_IO, SG_DEBUG,
"Saving user settings to " << tmpFile);
921 writeProperties(tmpFile,
globals->get_props(),
false, SGPropertyNode::USERARCHIVE);
922 tmpFile.rename(autosaveFile);
923 SG_LOG(SG_IO, SG_INFO,
"Saved user settings to " << autosaveFile);
924 }
catch (
const sg_exception &e) {
942 return fgGetInt(
"/sim/time/warp-delta");
947 fgSetInt(
"/sim/time/warp-delta", d);
952 return subsystem_mgr->get_subsystem<
FGScenery>();
957 return subsystem_mgr->get_subsystem<
FGViewMgr>();
973 return subsystem_mgr->get_subsystem<
FGControls>();
978 _listeners_to_cleanup.push_back(l);
981void FGGlobals::cleanupListeners()
983 SGPropertyChangeListenerVec::iterator
i = _listeners_to_cleanup.begin();
984 for (;
i != _listeners_to_cleanup.end(); ++
i) {
987 _listeners_to_cleanup.clear();
989 simgear::AtomicChangeListener::clearPendingChanges();
994 return _packageRoot.get();
virtual SGPath resolve(const std::string &aResource, SGPath &) const
AircraftResourceProvider()
virtual SGPath resolve(const std::string &aResource, SGPath &) const
CurrentAircraftDirProvider()
Bucket for subsystem pointers representing the sim's state.
flightgear::View * get_current_view() const
void set_catalog_aircraft_path(const SGPath &path)
specify a path we'll prepend to the aircraft paths list if non-empty.
void setPackageRoot(const SGSharedPtr< simgear::pkg::Root > &p)
simgear::pkg::Root * packageRoot()
SGPath resolve_maybe_aircraft_path(const std::string &branch) const
Same as above, but test for non 'Aircraft/' branch paths, and always resolve them against fg_root.
void set_warp_delta(long int d)
long int get_warp() const
PathList get_aircraft_paths() const
void set_warp(long int w)
SGVec3d get_view_position_cart() const
void append_aircraft_path(const SGPath &path)
Add an aircraft directory.
void set_renderer(FGRenderer *render)
virtual FGRenderer * get_renderer() const
void append_fg_scenery(const SGPath &scenery)
Add a scenery directory.
SGGeod get_aircraft_position() const
void set_matlib(SGMaterialLib *m)
void get_aircraft_orientation(double &heading, double &pitch, double &roll)
PathList get_data_paths() const
Get list of data locations.
void append_aircraft_paths(const PathList &path)
SGEventMgr * get_event_mgr() const
SGPath resolve_aircraft_path(const std::string &branch) const
Given a path to an aircraft-related resource file, resolve it against the appropriate root.
void set_download_dir(const SGPath &path)
FGControls * get_controls() const
SGSubsystemMgr * get_subsystem_mgr() const
void resetPropertyRoot()
reset the property tree to new, empty tree.
SGVec3d get_aircraft_position_cart() const
void set_fg_home(const SGPath &home)
bool is_headless()
A runtime headless mode for running without a GUI.
SGVec3d get_ownship_reference_position_cart() const
void loadUserSettings(SGPath userDatapath=SGPath())
Load user settings from the autosave file under 'userDataPath', which defaults to $FG_HOME.
SGGeod get_view_position() const
void append_read_allowed_paths(const SGPath &path)
Allow Nasal to read a path.
SGPath resolve_resource_path(const std::string &branch) const
Search in the following directories:
void append_data_path(const SGPath &path, bool afterFGRoot=false)
FGScenery * get_scenery() const
void addListenerToCleanup(SGPropertyChangeListener *l)
void set_terrasync_dir(const SGPath &path)
const SGPath & get_fg_home() const
SGPath autosaveFilePath(SGPath userDataPath=SGPath()) const
Return an SGPath instance for the autosave file under 'userDataPath', which defaults to $FG_HOME.
void set_fg_root(const SGPath &root)
void saveUserSettings(SGPath userDatapath=SGPath())
Save user settings to the autosave file under 'userDataPath', which defaults to $FG_HOME.
T * get_subsystem() const
long int get_warp_delta() const
SGPath findDataPath(const std::string &pathSuffix) const
Given a path suffix (eg 'Textures' or 'AI/Traffic'), find the first data directory which defines it.
FGViewMgr * get_viewmgr() const
void set_headless(bool mode)
void set_texture_cache_dir(const SGPath &textureCache)
static const char * staticSubsystemClassId()
flightgear::View * get_current_view()
bool fgLoadProps(const std::string &path, SGPropertyNode *props, bool in_fg_root, int default_mode)
Load properties from a file.
int fgGetInt(const char *name, int defaultValue)
Get an int value for a property.
std::string fgGetString(const char *name, const char *defaultValue)
Get a string value for a property.
bool fgSetInt(const char *name, int val)
Set an int value for a property.
static void tryAutosaveMigration(const SGPath &userDataPath, SGPropertyNode *props)
static bool operator<(const VersionPair &a, const VersionPair &b)
std::pair< int, int > VersionPair
static void treeDumpRefCounts(int depth, SGPropertyNode *nd)
static std::string autosaveName()
static void treeClearAliases(SGPropertyNode *nd)
static void deleteProperties(SGPropertyNode *props, const string_list &blacklist)
std::vector< SGPath > PathList
std::vector< std::string > string_list
void guiErrorMessage(const char *txt)
FlightGear Localization Support.
void setHeadlessMode(bool headless)
bool fgGetBool(char const *name, bool def)
Get a bool value for a property.
bool fgSetBool(char const *name, bool val)
Set a bool value for a property.
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.