FlightGear next
fg_init.cxx
Go to the documentation of this file.
1/*
2 * SPDX-FileName: fg_init.cxx
3 * SPDX-FileComment: FlightGear top level initialization routines
4 * SPDX-FileCopyrightText: Copyright (C) 1997 Curtis L. Olson - http://www.flightgear.org/~curt
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8#include <config.h>
9
10#include <simgear/compiler.h>
11
12#include <cstdio>
13#include <cstdlib>
14#include <cstring> // strcmp()
15
16#include "cJSON.h"
17
18#if defined(SG_WINDOWS)
19#define _WINSOCKAPI_
20# include <io.h> // isatty()
21# include <process.h> // _getpid()
22# include <Windows.h>
23# define isatty _isatty
24#else
25// for open() and options
26# include <sys/types.h>
27# include <sys/stat.h>
28# include <fcntl.h>
29# include <sys/file.h>
30#endif
31
32#include <string>
33
34#include <osgViewer/Viewer>
35
36#include <simgear/canvas/Canvas.hxx>
37#include <simgear/constants.h>
38#include <simgear/debug/logstream.hxx>
39#include <simgear/structure/commands.hxx>
40#include <simgear/structure/exception.hxx>
41#include <simgear/structure/event_mgr.hxx>
42#include <simgear/structure/SGPerfMon.hxx>
43#include <simgear/misc/sg_path.hxx>
44#include <simgear/misc/sg_dir.hxx>
45#include <simgear/io/iostreams/sgstream.hxx>
46#include <simgear/misc/strutils.hxx>
47#include <simgear/embedded_resources/EmbeddedResourceManager.hxx>
48#include <simgear/props/props_io.hxx>
49#include <simgear/scene/tsync/terrasync.hxx>
50#include <simgear/timing/sg_time.hxx>
51
52#include <simgear/scene/material/Effect.hxx>
53#include <simgear/scene/material/matlib.hxx>
54#include <simgear/scene/model/modellib.hxx>
55#include <simgear/scene/model/particles.hxx>
56#include <simgear/scene/tgdb/TreeBin.hxx>
57#include <simgear/scene/tgdb/userdata.hxx>
58#include <simgear/scene/tgdb/VPBTechnique.hxx>
59#include <simgear/scene/tsync/terrasync.hxx>
60
61#ifdef SG_TORRENT
62#include <simgear/io/torrent.hxx>
63#endif
64
65#include <simgear/package/Root.hxx>
66#include <simgear/package/Package.hxx>
67#include <simgear/package/Install.hxx>
68#include <simgear/package/Catalog.hxx>
69
71#include <Aircraft/controls.hxx>
72#include <Aircraft/replay.hxx>
75
76#include <Airports/runways.hxx>
77#include <Airports/airport.hxx>
78#include <Airports/dynamics.hxx>
80
81#include <ATC/atc_mgr.hxx>
82
85
86#include <Canvas/canvas_mgr.hxx>
87#include <Canvas/gui_mgr.hxx>
89#include <GUI/new_gui.hxx>
90#include <GUI/Highlight.hxx>
91#include <GUI/MessageBox.hxx>
92#include <Input/input.hxx>
94#include <Model/acmodel.hxx>
95#include <Model/modelmgr.hxx>
96#include <AIModel/submodel.hxx>
97#include <AIModel/AIManager.hxx>
99#include <Main/locale.hxx>
100#include <Navaids/navdb.hxx>
101#include <Navaids/navlist.hxx>
102#include <Scenery/scenery.hxx>
104#include <Scripting/NasalSys.hxx>
105#include <Sound/voice.hxx>
106#include <Sound/soundmanager.hxx>
107#include <Systems/system_mgr.hxx>
108#include <Time/tide.hxx>
109#include <Time/light.hxx>
110#include <Time/TimeManager.hxx>
111
112#include <Traffic/TrafficMgr.hxx>
114#if defined(ENABLE_SWIFT)
116#endif
119#include <FDM/fdm_shell.hxx>
121#include <Network/DNSClient.hxx>
122#include <Network/HTTPClient.hxx>
123#include <Network/fgcom.hxx>
124#include <Network/http/httpd.hxx>
125#include <Viewer/CameraGroup.hxx>
128#include <Viewer/renderer.hxx>
129#include <Viewer/splash.hxx>
130#include <Viewer/viewmgr.hxx>
131
133#include "FGInterpolator.hxx"
134#include "fg_init.hxx"
135#include "fg_io.hxx"
136#include "fg_props.hxx"
137#include "globals.hxx"
138#include "logger.hxx"
139#include "main.hxx"
140#include "options.hxx"
141#include "positioninit.hxx"
142#include "util.hxx"
143#include <Main/ErrorReporter.hxx>
145
146#if defined(SG_MAC)
147#include <GUI/CocoaHelpers.h> // for Mac impl of platformDefaultDataPath()
148#endif
149
150using std::endl;
151using std::string;
152
153using namespace simgear::pkg;
154
155// Return the current base package version
156string fgBasePackageVersion(const SGPath& base_path) {
157 SGPath p(base_path);
158 p.append("version");
159 if (!p.exists()) {
160 return string();
161 }
162
163 sg_gzifstream in( p );
164 if (!in.is_open()) {
165 return string();
166 }
167
168 string version;
169 in >> version;
170
171 return version;
172}
173
174std::optional<FGBasePackageInfo> fgBasePackageInfo(const SGPath& path)
175{
176 SGPath p = path / "base_package.json";
177 if (!p.exists()) {
178 return {};
179 }
180
181 sg_ifstream in(p);
182 if (!in.is_open()) {
183 return {};
184 }
185
186 const auto content = in.read_all();
187 cJSON* json = cJSON_Parse(content.c_str());
188
190 r.buildDate = cJSON_GetObjectItem(json, "build-date")->valuestring;
191 r.gitRevision = cJSON_GetObjectItem(json, "fgdata-sha")->valuestring;
192
193 cJSON_Delete(json);
194 return r;
195}
196
198{
199public:
200 FindAndCacheAircraft(SGPropertyNode* autoSave)
201 {
202 _cache = autoSave->getNode("sim/startup/path-cache", true);
203 }
204
205 void setDidUseLauncher(bool didUseLauncher)
206 {
207 _didUseLauncher = didUseLauncher;
208 }
209
217 {
218 const std::string aircraftDir = fgGetString("/sim/aircraft-dir", "");
219 if (aircraftDir.empty()) {
220 return false;
221 }
222
223 const std::string aircraft = fgGetString( "/sim/aircraft", "");
224 SGPath setFile = SGPath::fromUtf8(aircraftDir) / (aircraft + "-set.xml");
225 return setFile.exists();
226 }
227
229 {
230 std::string aircraft = fgGetString( "/sim/aircraft", "");
231 if (aircraft.empty()) {
233 "No aircraft was specified");
234 SG_LOG(SG_GENERAL, SG_ALERT, "no aircraft specified");
235 return false;
236 }
237
238 _searchAircraft = aircraft + "-set.xml";
239 std::string aircraftDir = fgGetString("/sim/aircraft-dir", "");
240 if (!aircraftDir.empty()) {
241 // aircraft-dir was set, skip any searching at all, if it's valid
242 simgear::Dir acPath(aircraftDir);
243 SGPath setFile = acPath.file(_searchAircraft);
244 if (setFile.exists()) {
245 SG_LOG(SG_GENERAL, SG_INFO, "found aircraft in dir: " << aircraftDir );
246
247 try {
248 readProperties(setFile, globals->get_props());
249 } catch ( const sg_exception &e ) {
250 SG_LOG(SG_IO, SG_ALERT,
251 "Error reading aircraft: " << e.getFormattedMessage());
252 flightgear::addSentryBreadcrumb("Aircraft-dir=" + aircraftDir, "error");
253 SG_LOG(SG_IO, SG_ALERT, "aircraft dir is:" << aircraftDir);
255 "Error reading aircraft",
256 "An error occured reading the requested aircraft (" + aircraft + ")",
257 e.getFormattedMessage());
258 return false;
259 }
260
261 checkAircraftMinVersion();
262 checkAircraftDirName();
263
264 // apply state after the -set.xml, but before any options are are set
266 return true;
267 } else {
268 SG_LOG(SG_GENERAL, SG_ALERT, "aircraft '" << _searchAircraft <<
269 "' not found in specified dir:" << aircraftDir);
270 flightgear::addSentryBreadcrumb("Aircraft-dir=" + aircraftDir, "error");
272 "Aircraft not found",
273 "The requested aircraft (" + aircraft + ") could not be found "
274 "in the specified location. (" +
275 aircraftDir + ")",
276 aircraftDir);
277 return false;
278 }
279 }
280
281 if (!checkCache()) {
282 flightgear::addSentryBreadcrumb("Scanning aircraft paths", "info");
283
284 // prepare cache for re-scan
285 SGPropertyNode* n = _cache->getNode("fg-root", true);
286 n->setStringValue(globals->get_fg_root().utf8Str());
287 n->setAttribute(SGPropertyNode::USERARCHIVE, true);
288 n = _cache->getNode("fg-aircraft", true);
289 n->setStringValue(getAircraftPaths().c_str());
290 n->setAttribute(SGPropertyNode::USERARCHIVE, true);
291 _cache->removeChildren("aircraft");
292
294 }
295
296 if (_foundPath.isNull()) {
297 SG_LOG(SG_GENERAL, SG_ALERT,
298 "Cannot find the specified aircraft: '" << aircraft << "'");
299
300 flightgear::addSentryBreadcrumb("Aircraft paths: " + SGPath::join(globals->get_aircraft_paths(), ";"), "error");
301
302 SG_LOG(SG_GENERAL, SG_ALERT, "\tin paths:" << SGPath::join(globals->get_aircraft_paths(), ";"));
303
304 std::string notFoundMessage;
305 // don't report failures where the launcher was not used, to Sentry,
306 // since they are nearly all configuration problems.
307 bool reportToSentry = _didUseLauncher;
308
309 if (globals->get_aircraft_paths().empty()) {
310 notFoundMessage = "The requested aircraft (" + aircraft + ") could not be found. No aircraft paths are configured.";
311 reportToSentry = false; // no need to log these
312 } else {
313 notFoundMessage = "The requested aircraft (" + aircraft + ") could not be found in any of the search paths.";
314 }
315
317 "Aircraft not found",
318 notFoundMessage,
319 {},
320 reportToSentry);
321 return false;
322 }
323
324 SG_LOG(SG_GENERAL, SG_INFO, "Loading aircraft -set file from:" << _foundPath);
325 fgSetString( "/sim/aircraft-dir", _foundPath.dir().c_str());
326 if (!_foundPath.exists()) {
327 SG_LOG(SG_GENERAL, SG_ALERT, "Unable to find -set file:" << _foundPath);
328 return false;
329 }
330
331
332 try {
333 readProperties(_foundPath, globals->get_props());
334 } catch ( const sg_exception &e ) {
335 SG_LOG(SG_INPUT, SG_ALERT,
336 "Error reading aircraft: " << e.getFormattedMessage());
338 "Error reading aircraft",
339 "An error occured reading the requested aircraft (" + aircraft + ")",
340 e.getFormattedMessage());
341 return false;
342 }
343
344 // apply state after the -set.xml, but before any options are are set
346
347 checkAircraftMinVersion();
348 checkAircraftDirName();
349
350 return true;
351 }
352
353private:
354 std::string getAircraftPaths()
355 {
356 return SGPath::join(globals->get_aircraft_paths(), ";");
357 }
358
359 bool checkCache()
360 {
361 if (globals->get_fg_root().utf8Str() != _cache->getStringValue("fg-root", "")) {
362 return false; // cache mismatch
363 }
364
365 if (getAircraftPaths() != _cache->getStringValue("fg-aircraft", "")) {
366 return false; // cache mismatch
367 }
368
369 std::vector<SGPropertyNode_ptr> cache = _cache->getChildren("aircraft");
370 for (unsigned int i = 0; i < cache.size(); i++) {
371 const std::string name = cache[i]->getStringValue("file", "");
372 if (!simgear::strutils::iequals(_searchAircraft, name)) {
373 continue;
374 }
375
376 SGPath xml(cache[i]->getStringValue("path", ""));
377 xml.append(name);
378 if (xml.exists()) {
379 flightgear::addSentryBreadcrumb("Found aircraft via cache", "info");
380 _foundPath = xml;
381 return true;
382 }
383
384 return false;
385 } // of aircraft in cache iteration
386
387 return false;
388 }
389
390 virtual VisitResult visit(const SGPath& p)
391 {
392 SGPath realPath = p.realpath();
393 // create cache node
394 int i = 0;
395 while (1) {
396 if (!_cache->getChild("aircraft", i++, false))
397 break;
398 }
399
400 SGPropertyNode *n, *entry = _cache->getChild("aircraft", --i, true);
401
402 std::string fileName(realPath.file());
403 n = entry->getNode("file", true);
404 n->setStringValue(fileName);
405 n->setAttribute(SGPropertyNode::USERARCHIVE, true);
406
407 n = entry->getNode("path", true);
408 n->setStringValue(realPath.dir());
409 n->setAttribute(SGPropertyNode::USERARCHIVE, true);
410
411 if (simgear::strutils::iequals(fileName, _searchAircraft)) {
412 _foundPath = realPath;
413 return VISIT_DONE;
414 }
415
416 return VISIT_CONTINUE;
417 }
418
419 bool checkAircraftMinVersion()
420 {
421 SGPropertyNode* minVersionNode = globals->get_props()->getNode("/sim/minimum-fg-version");
422 if (minVersionNode) {
423 std::string minVersion = minVersionNode->getStringValue();
424 const int c = simgear::strutils::compare_versions(FLIGHTGEAR_VERSION, minVersion, 2);
425 if (c < 0) {
426 SG_LOG(SG_AIRCRAFT, SG_DEV_ALERT, "Aircraft minimum version (" << minVersion <<
427 ") is higher than FG version:" << FLIGHTGEAR_VERSION);
428 flightgear::modalMessageBox("Aircraft requires newer version of FlightGear",
429 "The selected aircraft requires FlightGear version " + minVersion
430 + " to work correctly. Some features may not work as expected, or the aircraft may not load at all.");
431 return false;
432 }
433 } else {
434 SG_LOG(SG_AIRCRAFT, SG_DEV_ALERT, "Aircraft does not specify a minimum FG version: please add one at /sim/minimum-fg-version");
435 }
436
437 auto compatNodes = globals->get_props()->getNode("/sim")->getChildren("compatible-fg-version");
438 if (!compatNodes.empty()) {
439 bool showCompatWarning = true;
440
441 // if we have at least one compatibility node, then it needs to match
442 for (const auto& cn : compatNodes) {
443 const auto v = cn->getStringValue();
444 if (simgear::strutils::compareVersionToWildcard(FLIGHTGEAR_VERSION, v)) {
445 showCompatWarning = false;
446 break;
447 }
448 }
449
450 if (showCompatWarning) {
451 flightgear::modalMessageBox("Aircraft not compatible with this version",
452 "The selected aircraft has not been checked for compatability with this version of FlightGear (" FLIGHTGEAR_VERSION "). "
453 "Some aircraft features might not work, or might be displayed incorrectly.");
454 }
455 }
456
457 return true;
458 }
459
460 bool checkAircraftDirName()
461 {
462 auto expectedDirNode = globals->get_props()->getNode("/sim/expected-aircraft-dir-name");
463 const string aircraftId = fgGetString("/sim/aircraft");
464 if (aircraftId != fgGetString("/sim/aircraft-id")){
465 // Skip the check for aircraft installed from catalog.
466 return true;
467 }
468
469 if (expectedDirNode) {
470 const string expectedDir = expectedDirNode->getStringValue();
471 const SGPath dir(fgGetString("/sim/aircraft-dir"));
472 if (dir.file() != expectedDirNode->getStringValue()) {
473 flightgear::fatalMessageBoxThenExit("Aircraft folder named incorrectly",
474 "The folder of the selected aircraft must be named '" + expectedDir +
475 "' (instead of '" + dir.file() + "') to work correctly. If you downloaded it yourself, " +
476 "please ensure the folder is called '" +
477 expectedDir + "' and re-name if necessary.");
478 return false;
479 }
480 } else {
481 // TODO Uncomment for the next release.
482 //SG_LOG(SG_AIRCRAFT, SG_DEV_ALERT, "Aircraft does not specify the required aircraft directory name: please add one at /sim/expected-aircraft-dir-name");
483 }
484 return true;
485 }
486
487 std::string _searchAircraft;
488 SGPath _foundPath;
489 SGPropertyNode* _cache = nullptr;
490 bool _didUseLauncher = false;
491};
492
493#ifdef _WIN32
494static SGPath platformDefaultDataPath()
495{
496 SGPath appDataPath = SGPath::fromEnv("APPDATA");
497
498 if (appDataPath.isNull()) {
500 "FlightGear", "Unable to get the value of APPDATA.",
501 "FlightGear is unable to retrieve the value of the APPDATA environment "
502 "variable. This is quite unexpected on Windows platforms, and FlightGear "
503 "can't continue its execution without this value, sorry.");
504 }
505
506 return appDataPath / "flightgear.org";
507}
508
509#elif defined(SG_MAC)
510
511// platformDefaultDataPath defined in GUI/CocoaHelpers.h
512
513#else
515{
516 return SGPath::home() / ".fgfs";
517}
518#endif
519
520#if defined(SG_WINDOWS)
521static HANDLE static_fgHomeWriteMutex = nullptr;
522#endif
523
525{
526 return SGPath::fromEnv("FG_HOME", platformDefaultDataPath());
527}
528
530{
531 SGPath dataPath = fgHomePath();
532 globals->set_fg_home(dataPath);
533
534 simgear::Dir fgHome(dataPath);
535 if (!fgHome.exists()) {
536 fgHome.create(0755);
537 }
538
539 if (!fgHome.exists()) {
541 "Problem setting up user data",
542 "Unable to create the user-data storage folder at '" +
543 dataPath.utf8Str() + "'.");
544 return InitHomeAbort;
545 }
546
547 if (fgGetBool("/sim/fghome-readonly", false)) {
548 // user / config forced us into readonly mode, fine
549 SG_LOG(SG_GENERAL, SG_INFO, "Running with FG_HOME readonly");
551 }
552
554#if defined(SG_WINDOWS)
555 // don't use a PID file on Windows, because deleting on close is
556 // unreliable and causes false-positives. Instead, use a named
557 // mutex.
558
559 static_fgHomeWriteMutex = CreateMutexA(nullptr, FALSE, "org.flightgear.fgfs.primary");
560 if (static_fgHomeWriteMutex == nullptr) {
561 printf("CreateMutex error: %d\n", GetLastError());
562 SG_LOG(SG_GENERAL, SG_ALERT, "Failed to create mutex for multi-app protection");
563 return InitHomeAbort;
564 } else if (GetLastError() == ERROR_ALREADY_EXISTS) {
565 SG_LOG(SG_GENERAL, SG_POPUP, "flightgear instance already running, switching to FG_HOME read-only.");
566 fgSetBool("/sim/fghome-readonly", true);
567 return InitHomeReadOnly;
568 } else {
569 SG_LOG(SG_GENERAL, SG_INFO, "Created multi-app mutex, we are in writeable mode");
570 result = InitHomeOkay;
571 }
572#else
573// write our PID, and check writeability
574 SGPath pidPath(dataPath, "fgfs_lock.pid");
575 std::string ps = pidPath.utf8Str();
576
577 if (pidPath.exists()) {
578 int fd = ::open(ps.c_str(), O_RDONLY, 0644);
579 if (fd < 0) {
580 SG_LOG(SG_GENERAL, SG_ALERT, "failed to open local file:" << pidPath
581 << "\n\tdue to:" << simgear::strutils::error_string(errno));
582 return InitHomeAbort;
583 }
584
585 int err = ::flock(fd, LOCK_EX | LOCK_NB);
586 if (err < 0) {
587 if ( errno == EWOULDBLOCK) {
588 SG_LOG(SG_GENERAL, SG_ALERT, "flightgear instance already running, switching to FG_HOME read-only. ");
589 SG_LOG(SG_GENERAL, SG_ALERT, "Couldn't flock() file at:" << pidPath);
590
591 // set a marker property so terrasync/navcache don't try to write
592 // from secondary instances
593 fgSetBool("/sim/fghome-readonly", true);
594 return InitHomeReadOnly;
595 } else {
596 SG_LOG(SG_GENERAL, SG_ALERT, "failed to lock file:" << pidPath
597 << "\n\tdue to:" << simgear::strutils::error_string(errno));
598 return InitHomeAbort;
599 }
600 }
601
602 // we locked it!
603 result = InitHomeOkay;
604 } else {
605 char buf[16];
606 std::string ps = pidPath.utf8Str();
607
608 ssize_t len = snprintf(buf, 16, "%d\n", getpid());
609 int fd = ::open(ps.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
610 if (fd < 0) {
611 SG_LOG(SG_GENERAL, SG_ALERT, "failed to open local file:" << pidPath
612 << "\n\tdue to:" << simgear::strutils::error_string(errno));
613 return InitHomeAbort;
614 }
615
616 int err = write(fd, buf, len);
617 if (err < 0) {
618 SG_LOG(SG_GENERAL, SG_ALERT, "failed to write to lock file:" << pidPath
619 << "\n\tdue to:" << simgear::strutils::error_string(errno));
620 return InitHomeAbort;
621 }
622
623 err = flock(fd, LOCK_EX);
624 if (err != 0) {
625 SG_LOG(SG_GENERAL, SG_ALERT, "failed to lock file:" << pidPath
626 << "\n\tdue to:" << simgear::strutils::error_string(errno));
627 return InitHomeAbort;
628 }
629
630 result = InitHomeOkay;
631 }
632#endif
633 fgSetBool("/sim/fghome-readonly", false);
634 return result;
635}
636
638{
639#if defined(SG_WINDOWS)
640 if (static_fgHomeWriteMutex) {
641 CloseHandle(static_fgHomeWriteMutex);
642 }
643#else
644 if (fgGetBool("/sim/fghome-readonly") == false) {
645 SGPath pidPath = globals->get_fg_home() / "fgfs_lock.pid";
646 pidPath.remove();
647 }
648#endif
649}
650
652{
653#if defined(SG_WINDOWS)
654 // there's no file here, so we can't actually delete anything
655#else
656 SGPath pidPath = globals->get_fg_home() / "fgfs_lock.pid";
657 pidPath.remove();
658#endif
659}
660
661static void createBaseStorageDirForAddons(const SGPath& exportDir)
662{
663 SGPath addonStorageBasePath = exportDir / "Addons";
664 if (addonStorageBasePath.exists()) {
665 if (!addonStorageBasePath.isDir()) {
666 throw sg_error(
667 "Unable to create add-on storage base directory, because the entry "
668 "already exists but is not a directory: '" +
669 addonStorageBasePath.utf8Str() + "'");
670 }
671 } else {
672 simgear::Dir(addonStorageBasePath).create(0777); // respect user's umask
673 }
674}
675
676struct SimLogFileLine : SGPropertyChangeListener
677{
679 fgAddChangeListener(this, "/sim/log-file-line");
680 }
681 virtual void valueChanged(SGPropertyNode* node) {
682 bool fileLine = node->getBoolValue();
683 sglog().setFileLine(fileLine);
684 }
685};
686
687// Read in configuration (file and command line)
688int fgInitConfig ( int argc, char **argv, bool reinit )
689{
690 SGPath dataPath = globals->get_fg_home();
691
692 simgear::Dir exportDir(simgear::Dir(dataPath).file("Export"));
693 if (!exportDir.exists()) {
694 exportDir.create(0755);
695 }
696
697 // Reserve a directory where add-ons can write. There will be a subdir for
698 // each add-on, see Addon::createStorageDir() and Addon::getStoragePath().
699 createBaseStorageDirForAddons(exportDir.path());
700
701 // Set /sim/fg-home. Use FG_HOME if necessary.
702 // deliberately not a tied property, for SGPath::validate() security
703 // write-protect to avoid accidents
704 SGPropertyNode *home = fgGetNode("/sim", true);
705 home->removeChild("fg-home", 0);
706 home = home->getChild("fg-home", 0, true);
707 home->setStringValue(dataPath.utf8Str());
708 home->setAttribute(SGPropertyNode::WRITE, false);
709
712 if (!reinit) {
713 auto result = options->init(argc, argv, dataPath);
714 if (result != flightgear::FG_OPTIONS_OK) {
715 return result;
716 }
717 }
718
719 // establish default for developer-mode based upon compiled build types
720 bool developerMode = true;
721 if (!strcmp(FG_BUILD_TYPE, "Release")) {
722 developerMode = false;
723 }
724
725 // allow command line to override
726 const std::optional<bool> devOption = options->checkBoolOptionSet("developer");
727 if (devOption.has_value()) { // User specified the developer option
728 developerMode = devOption.value();
729 }
730
731 auto node = fgGetNode("/sim/developer-mode", true);
732 // ensure this value survives reset
733 node->setAttribute(SGPropertyNode::PRESERVE, true);
734 node->setBoolValue(developerMode);
735 sglog().setDeveloperMode(developerMode);
736
737 static SimLogFileLine simLogFileLine;
738
739 // Read global defaults from $FG_ROOT/defaults
740 SG_LOG(SG_GENERAL, SG_DEBUG, "Reading global defaults");
741 SGPath defaultsXML = globals->get_fg_root() / "defaults.xml";
742 if (!defaultsXML.exists()) {
744 "Missing file",
745 "Couldn't load an essential simulator data file.",
746 defaultsXML.utf8Str());
747 }
748
749 if(!fgLoadProps("defaults.xml", globals->get_props()))
750 {
752 "Corrupted file",
753 "Couldn't load an essential simulator data file as it is corrupted.",
754 defaultsXML.utf8Str());
755 }
756 SG_LOG(SG_GENERAL, SG_DEBUG, "Finished Reading global defaults");
757
758 // do not load user settings when reset to default is requested, or if
759 // told to explicitly ignore
760 if (options->isOptionSet("restore-defaults") || options->isOptionSet("ignore-autosave"))
761 {
762 SG_LOG(SG_GENERAL, SG_ALERT, "Ignoring user settings. Restoring defaults.");
763 } else {
764 globals->loadUserSettings(dataPath);
765 }
766
768}
769
771{
772 // deliberately not a tied property, for SGPath::validate() security
773 // write-protect to avoid accidents
774 SGPropertyNode* sim = fgGetNode("/sim", true);
775 sim->removeChildren("fg-aircraft");
776
777 int index = 0;
778 const PathList& aircraft_paths = globals->get_aircraft_paths();
779 for (PathList::const_iterator it = aircraft_paths.begin();
780 it != aircraft_paths.end(); ++it, ++index )
781 {
782 SGPropertyNode* n = sim->getChild("fg-aircraft", index, true);
783 n->setStringValue(it->utf8Str());
784 n->setAttribute(SGPropertyNode::WRITE, false);
785 }
786}
787
788void fgInitAircraftPaths(bool reinit)
789{
790 if (!globals->packageRoot()) {
792 }
793
794 SGSharedPtr<Root> pkgRoot(globals->packageRoot());
795 SGPropertyNode* aircraftProp = fgGetNode("/sim/aircraft", true);
796 aircraftProp->setAttribute(SGPropertyNode::PRESERVE, true);
797
798 if (!reinit) {
800 }
801}
802
803int fgInitAircraft(bool reinit, bool didUseLauncher)
804{
805 if (!reinit) {
808 return r;
809 }
810
811 FindAndCacheAircraft f(globals->get_props());
812 f.setDidUseLauncher(didUseLauncher);
813 const bool haveExplicit = f.haveExplicitAircraft();
814
815 SGSharedPtr<Root> pkgRoot(globals->packageRoot());
816 SGPropertyNode* aircraftProp = fgGetNode("/sim/aircraft", true);
817 SGPropertyNode* aircraftDirProp = fgGetNode("/sim/aircraft-dir", true);
818
819 // ensure aircraft-dir survives reset, so we find the same aircraft
820 // after a reset as we did prior to it. Without this, aircraft
821 // can unintentionally change on a reset
822 aircraftDirProp->setAttribute(SGPropertyNode::PRESERVE, true);
823
824 const string fullyQualifiedAircraftId = fgGetString("/sim/aircraft-id");
825 string aircraftId = fullyQualifiedAircraftId.empty() ? aircraftProp->getStringValue() : fullyQualifiedAircraftId;
826
827 flightgear::addSentryTag("aircraft", aircraftId);
828
829 PackageRef acftPackage;
830 if (!haveExplicit) {
831 acftPackage = pkgRoot->getPackageById(aircraftId);
832 }
833
834 if (acftPackage) {
835 if (acftPackage->isInstalled()) {
836 SG_LOG(SG_GENERAL, SG_INFO, "Loading aircraft from package:" << acftPackage->qualifiedId());
837 // if we resolved a non-qualified ID, set the full one back to /sim/aircraft-id
838 fgSetString("/sim/aircraft-id", acftPackage->qualifiedId());
839
840 // replace this tag, so we know which hangar is in use
841 flightgear::addSentryTag("aircraft", acftPackage->qualifiedId());
842
843 // set catalog path so intra-package dependencies within the catalog
844 // are resolved correctly.
845 globals->set_catalog_aircraft_path(acftPackage->catalog()->installRoot());
846
847 // set aircraft-dir to short circuit the search process
848 InstallRef acftInstall = acftPackage->install();
849 fgSetString("/sim/aircraft-dir", acftInstall->path().utf8Str());
850
851 // overwrite the fully qualified ID with the aircraft one, so the
852 // code in FindAndCacheAircraft works as normal
853 // note since we may be using a variant, we can't use the package ID
854 size_t lastDot = aircraftId.rfind('.');
855 if (lastDot != std::string::npos) {
856 aircraftId = aircraftId.substr(lastDot + 1);
857 }
858 aircraftProp->setStringValue(aircraftId);
859
860 // run the traditional-code path below
861 } else {
862#if 0
863 // naturally the better option would be to on-demand install it!
865 "Aircraft not installed",
866 "Requested aircraft is not currently installed.",
867 aircraftId);
868
870#endif
871 // fall back the default aircraft instead
872 }
873 }
874
876
877 if (!f.loadAircraft()) {
879 }
880
882}
883
891bool
893{
895 static bool doingRebuild = false;
896
897 if (!cache) {
899 doingRebuild = cache->isRebuildRequired();
900 }
901
902 static const char* splashIdentsByRebuildPhase[] = {
903 "loading-nav-dat",
904 "navdata-reading-apt-dat-files",
905 "navdata-loading-airports",
906 "navdata-navaids",
907 "navdata-fixes",
908 "navdata-pois"
909 };
910
911 if (doingRebuild) {
913 phase = cache->rebuild();
915 // update the splash text based on percentage, phase
916
917 fgSplashProgress(splashIdentsByRebuildPhase[phase],
919
920 // sleep to give the rebuild thread more time
921 SGTimeStamp::sleepForMSec(50);
922 return false;
923 }
924 }
925
926 FGTACANList *channellist = new FGTACANList;
927 globals->set_channellist( channellist );
928
929 SGPath path(globals->get_fg_root());
930 path.append( "Navaids/TACAN_freq.dat" );
931 flightgear::NavLoader().loadTacan(path, channellist);
932
933 return true;
934}
935
936// General house keeping initializations
938
939 SG_LOG( SG_GENERAL, SG_INFO, "General Initialization" );
940 SG_LOG( SG_GENERAL, SG_INFO, "======= ==============" );
941
942 if ( globals->get_fg_root().isNull() ) {
943 // No root path set? Then bail ...
944 SG_LOG( SG_GENERAL, SG_ALERT,
945 "Cannot continue without a path to the base package "
946 << "being defined." );
947 return false;
948 }
949 SG_LOG( SG_GENERAL, SG_INFO, "FG_ROOT = " << '"' << globals->get_fg_root() << '"' << endl );
950
951 // Note: browser command is hard-coded for Mac/Windows, so this only affects other platforms
952 globals->set_browser(fgGetString("/sim/startup/browser-app", WEB_BROWSER));
953 fgSetString("/sim/startup/browser-app", globals->get_browser());
954
955 simgear::Dir cwd(simgear::Dir::current());
956 SGPropertyNode *curr = fgGetNode("/sim", true);
957 curr->removeChild("fg-current", 0);
958 curr = curr->getChild("fg-current", 0, true);
959 curr->setStringValue(cwd.path().utf8Str());
960 curr->setAttribute(SGPropertyNode::WRITE, false);
961
962 fgSetBool("/sim/startup/stdout-to-terminal", isatty(1) != 0 );
963 fgSetBool("/sim/startup/stderr-to-terminal", isatty(2) != 0 );
964
965 sgUserDataInit( globals->get_props() );
966 flightgear::addSentryTag("have-reset", "no");
967
968 return true;
969}
970
971// Write various configuraton values out to the logs
973{
974 SG_LOG( SG_GENERAL, SG_INFO, "Configuration State" );
975 SG_LOG( SG_GENERAL, SG_INFO, "============= =====" );
976
977 SG_LOG( SG_GENERAL, SG_INFO, "aircraft-dir = " << '"' << fgGetString("/sim/aircraft-dir") << '"' );
978 SG_LOG( SG_GENERAL, SG_INFO, "fghome-dir = " << '"' << globals->get_fg_home() << '"');
979 SG_LOG( SG_GENERAL, SG_INFO, "download-dir = " << '"' << fgGetString("/sim/paths/download-dir") << '"' );
980 SG_LOG( SG_GENERAL, SG_INFO, "terrasync-dir = " << '"' << fgGetString("/sim/terrasync/scenery-dir") << '"' );
981
982 SG_LOG( SG_GENERAL, SG_INFO, "aircraft-search-paths = \n\t" << SGPath::join(globals->get_aircraft_paths(), "\n\t") );
983 SG_LOG( SG_GENERAL, SG_INFO, "scenery-search-paths = \n\t" << SGPath::join(globals->get_fg_scenery(), "\n\t") );
984}
985
986#ifdef SG_TORRENT
987static simgear::HTTP::Client* s_getHttpClient()
988{
989 return globals->get_subsystem_mgr()->get_subsystem<FGHTTPClient>()->client();
990}
991#endif
992
993// This is the top level init routine which calls all the other
994// initialization routines. If you are adding a subsystem to flight
995// gear, its initialization call should located in this routine.
996// Returns non-zero if a problem encountered.
997void fgCreateSubsystems(bool duringReset) {
998
999 SG_LOG( SG_GENERAL, SG_INFO, "== Creating Subsystems");
1000
1001 // Fetch the subsystem manager.
1002 auto mgr = globals->get_subsystem_mgr();
1003
1004 globals->get_event_mgr()->init();
1005 globals->get_event_mgr()->setRealtimeProperty(fgGetNode("/sim/time/delta-realtime-sec", true));
1006
1007 // SGSubsystemMgr::INIT
1008 {
1009 // Initialize the property interpolator subsystem. Put into the INIT
1010 // group because the "nasal" subsystem may need it at GENERAL take-down.
1011 mgr->add<FGInterpolator>();
1012 mgr->add<Highlight>();
1013 mgr->add<NewGUI>();
1014 }
1015
1016 // SGSubsystemMgr::GENERAL
1017 {
1018 mgr->add<FGProperties>();
1020 mgr->add("performance-mon",
1021 new SGPerformanceMonitor(
1022 mgr,
1023 fgGetNode("/sim/performance-monitor", true)
1024 )
1025 );
1026
1027 // Initialize the material property subsystem.
1028 SGPath mpath( globals->get_fg_root() );
1029 mpath.append( fgGetString("/sim/rendering/materials-file") );
1030 if ( ! globals->get_matlib()->load(globals->get_fg_root(), mpath, globals->get_props()) ) {
1031 throw sg_io_exception("Error loading materials file", mpath);
1032 }
1033
1034 // may exist already due to GUI startup or --load-tape=http...
1035 if (!mgr->get_subsystem<FGHTTPClient>()) {
1036 mgr->add<FGHTTPClient>();
1037 }
1038 mgr->add<FGDNSClient>();
1039
1040 #ifdef SG_TORRENT
1041 mgr->add(simgear::Torrent::staticSubsystemClassId(),
1042 new simgear::Torrent(
1043 fgGetNode("/sim/torrent", true),
1044 s_getHttpClient
1045 )
1046 );
1047 #endif
1048
1049 // Initialize the weather modeling subsystem
1050 mgr->add<FGEnvironmentMgr>();
1051 mgr->add<Ephemeris>();
1052 mgr->add("xml-proprules", FGXMLAutopilotGroup::createInstance("property-rule"));
1053
1054 mgr->add<FGRouteMgr>();
1055 mgr->add<FGIO>();
1056 mgr->add<FGLogger>();
1057
1058 mgr->add<FGControls>();
1059 mgr->add<FGInput>();
1060 mgr->add<FGFlightHistory>();
1061
1062 {
1064 if( NULL != httpd )
1065 mgr->add("httpd", httpd);
1066 }
1067
1068 if (!duringReset) {
1069 mgr->add<FGTide>();
1070 }
1071 }
1072
1073 // SGSubsystemMgr::FDM
1074 {
1075 mgr->add<FDMShell>();
1076
1077 // Initialize the aircraft systems and instrumentation (before the
1078 // autopilot.)
1079 mgr->add<FGSystemMgr>();
1080 mgr->add<FGInstrumentMgr>();
1081 mgr->add("xml-autopilot", FGXMLAutopilotGroup::createInstance("autopilot"), SGSubsystemMgr::FDM);
1082 }
1083
1084 // SGSubsystemMgr::POST_FDM
1085 {
1086 mgr->add<PerformanceDB>();
1087 mgr->add<FGATCManager>();
1088 mgr->add<FGAIManager>();
1089 mgr->add<FGMultiplayMgr>();
1090
1091 #ifdef ENABLE_SWIFT
1093 #endif
1094
1095 // FGReplay.
1096 //
1097 // FGReplay is after FGMultiplayMgr, so that it can record the most
1098 // recent MP packets.
1099 //
1100 // FGReplay is before FGAIManager so that, when replaying, it can push
1101 // MP packets into FGMultiplayMgr's queue before FGAIManager reads from
1102 // this queue.
1103 //
1104 // We also call FGReplay::init() method here, to work around a
1105 // problem where JSBSim appears to rely on FGReplay creating certain
1106 // properties before it is initialised. This caused problems when
1107 // FGReplay was changed to be POST_FDM.
1108 mgr->add<FGReplay>();
1109 mgr->get_subsystem<FGReplay>()->init(); // Special case.
1110
1111 //mgr->add<FGAIManager>();
1112 mgr->add<FGSubmodelMgr>();
1113
1114 // It's probably a good idea to initialize the top level traffic manager
1115 // After the AI and ATC systems have been initialized properly.
1116 // AI Traffic manager
1117 mgr->add<FGTrafficManager>();
1118
1119 fgSetArchivable("/sim/panel/visibility");
1120 fgSetArchivable("/sim/panel/x-offset");
1121 fgSetArchivable("/sim/panel/y-offset");
1122 fgSetArchivable("/sim/panel/jitter");
1123 }
1124
1125 // SGSubsystemMgr::DISPLAY
1126 {
1127 simgear::canvas::Canvas::setSystemAdapter(
1128 simgear::canvas::SystemAdapterPtr(new canvas::FGCanvasSystemAdapter)
1129 );
1130 mgr->add<CanvasMgr>();
1131
1132 auto canvasGui = new GUIMgr;
1133 mgr->add("CanvasGUI", canvasGui, SGSubsystemMgr::DISPLAY);
1135 canvasGui->setGUIViewAndCamera(globals->get_renderer()->getView(), guiCamera);
1136
1137 #ifdef ENABLE_AUDIO_SUPPORT
1138 mgr->add<FGVoiceMgr>();
1139 #endif
1140
1141 // ordering here is important : Nasal (via events), then models, then views
1142 if (!duringReset) {
1143 mgr->add<FGLight>();
1144 mgr->add("events", globals->get_event_mgr(), SGSubsystemMgr::DISPLAY);
1145 }
1146
1147 mgr->add<FGAircraftModel>();
1148 mgr->add<FGModelMgr>();
1149 mgr->add<FGViewMgr>();
1150 }
1151
1152 // SGSubsystemMgr::SOUND
1153 {
1154 // Sound manager uses an own subsystem group "SOUND" which is the last
1155 // to be updated in every loop.
1156 // Sound manager is updated last so it can use the CPU while the GPU
1157 // is processing the scenery (doubled the frame-rate for me) -EMH-
1158 mgr->add<FGSoundManager>();
1159
1160 #ifdef ENABLE_IAX
1161 // Initialize the FGCom subsystem.
1162 // very important this goes in the SOUND group, since IAXClient
1163 // depends on OpenAL, which is shutdown when the SOUND group
1164 // shutdown.
1165 // Sentry: FLIGHTGEAR-66
1166 mgr->add<FGCom>();
1167 #endif
1168 }
1169}
1170
1172{
1173 SGTimeStamp st;
1174 st.stamp();
1175
1176 // Fetch the subsystem manager.
1177 auto mgr = globals->get_subsystem_mgr();
1178
1180 // Initialize the Nasal interpreter.
1181 // Do this last, so that the loaded scripts see initialized state
1183 mgr->add<FGNasalSys>();
1184
1185 // initialize methods that depend on other subsystems.
1186 st.stamp();
1187 mgr->postinit();
1188 SG_LOG(SG_GENERAL, SG_INFO, "Subsystems postinit took:" << st.elapsedMSec());
1189
1191 // End of subsystem initialization.
1193
1194 fgSetBool("/sim/crashed", false);
1195 fgSetBool("/sim/initialized", true);
1196
1197 SG_LOG( SG_GENERAL, SG_INFO, endl);
1198}
1199
1200// re-position is a simplified version of the traditional (legacy)
1201// reset procedure. We only need to poke systems which will be upset by
1202// a sudden change in aircraft position. Since this potentially includes
1203// Nasal, we trigger the 'reinit' signal.
1205{
1206 SGPropertyNode *master_freeze = fgGetNode("/sim/freeze/master");
1207 SG_LOG( SG_GENERAL, SG_INFO, "fgStartReposition()");
1208
1209 flightgear::addSentryBreadcrumb("start reposition", "info");
1210
1211 // ensure we are frozen
1212 bool freeze = master_freeze->getBoolValue();
1213 if ( !freeze ) {
1214 master_freeze->setBoolValue(true);
1215 }
1216
1217 // set this signal so Nasal scripts can take action.
1218 fgSetBool("/sim/signals/reinit", true);
1219 fgSetBool("/sim/crashed", false);
1220
1221 // Fetch the subsystem manager.
1222 auto mgr = globals->get_subsystem_mgr();
1223
1224 mgr->get_subsystem<FDMShell>()->unbind();
1225
1226 // update our position based on current presets
1227 // this will mark position as needed finalized which we'll do in the
1228 // main-loop
1230
1231 auto terraSync = mgr->get_subsystem<simgear::SGTerraSync>();
1232 if (terraSync) {
1233 terraSync->reposition();
1234 }
1235
1236 // Initialize the FDM
1237 mgr->get_subsystem<FDMShell>()->reinit();
1238
1239 // reset replay buffers
1240 mgr->get_subsystem<FGReplay>()->reinit();
1241
1242 // ugly: finalizePosition waits for METAR to arrive for the new airport.
1243 // we don't re-init the environment manager here, since historically we did
1244 // not, and doing so seems to have other issues. All that's needed is to
1245 // schedule METAR fetch immediately, so it's available for finalizePosition.
1246 // So we manually extract the METAR-fetching component inside the environment
1247 // manager, and re-init that.
1248 auto envMgr = static_cast<SGSubsystemGroup*>(mgr->get_subsystem<FGEnvironmentMgr>());
1249 if (envMgr) {
1250 envMgr->get_subsystem("realwx")->reinit();
1251 }
1252
1253 // needed for parking assignment to work after reposition
1254 auto atcManager = mgr->get_subsystem<FGATCManager>();
1255 if (atcManager) {
1256 atcManager->reposition();
1257 }
1258
1259 // need to bind FDMshell again
1260 mgr->get_subsystem<FDMShell>()->bind();
1261
1262 // need to reset aircraft (systems/instruments/autopilot)
1263 // so they can adapt to current environment
1264 mgr->get_subsystem<FGSystemMgr>()->reinit();
1265 mgr->get_subsystem<FGInstrumentMgr>()->reinit();
1266 mgr->get_subsystem("xml-autopilot")->reinit();
1267
1268 // need to update the timezone
1269 auto timeManager = mgr->get_subsystem<TimeManager>();
1270 if (timeManager) {
1271 timeManager->reposition();
1272 }
1273
1274 // setup state to end re-init
1275 fgSetBool("/sim/signals/reinit", false);
1276 if ( !freeze ) {
1277 master_freeze->setBoolValue(false);
1278 }
1279 fgSetBool("/sim/sceneryloaded",false);
1280 flightgear::addSentryBreadcrumb("end of reposition", "info");
1281}
1282
1284{
1285 flightgear::updateSentryTag("have-reset", "yes");
1286
1287 // save user settings now, so that USERARCIVE-d values changes since the
1288 // last init are recorded and hence re-loaded when we fgInitConfig down
1289 // later in this function. Otherwise all such settings are lost.
1290 globals->saveUserSettings();
1291
1292 SGPropertyNode_ptr preserved(new SGPropertyNode);
1293
1294 if (!copyPropertiesWithAttribute(globals->get_props(), preserved, SGPropertyNode::PRESERVE))
1295 SG_LOG(SG_GENERAL, SG_ALERT, "Error saving preserved state");
1296
1297 fgSetBool("/sim/signals/reinit", true);
1298 fgSetBool("/sim/freeze/master", true);
1299
1300 // pause the osgDB requests right now, but more may appear; we will
1301 // clear and cancel further down once shutdown has occured
1302 FGRenderer* render = globals->get_renderer();
1303 auto pager = render->getView()->getDatabasePager();
1304 pager->setAcceptNewDatabaseRequests(false);
1305 pager->cancel();
1306
1307 // extra clear is needed to ensure compile/merge lists are also empty
1308 pager->clear();
1309
1310 assert(pager->getDataToMergeListSize() == 0);
1311 assert(pager->getDataToCompileListSize() == 0);
1312
1313
1314 SGSubsystemMgr* subsystemManger = globals->get_subsystem_mgr();
1315 // Nasal is added in fgPostInit, ensure it's already shutdown
1316 // before other subsystems, so Nasal listeners don't fire during shutdown
1317 subsystemManger->remove("nasal");
1318
1319 subsystemManger->shutdown();
1320 subsystemManger->unbind();
1321
1322 // hack fix for many reset crashes relating to the static instance
1323 // of this class. Will be fixed better for future versions by making
1324 // this a proper subsystem.
1325 //FGATCDialogNew::hackyReset();
1326
1327 // remove most subsystems, with a few exceptions.
1328 for (int g=0; g<SGSubsystemMgr::MAX_GROUPS; ++g) {
1329 SGSubsystemGroup* grp = subsystemManger->get_group(static_cast<SGSubsystemMgr::GroupType>(g));
1330 for (auto nm : grp->member_names()) {
1331 if ((nm == "time") || (nm == "terrasync") || (nm == "events")
1332 || (nm == "lighting")
1335 )
1336 {
1337 continue;
1338 }
1339
1340 try {
1341 subsystemManger->remove(nm.c_str());
1342 } catch (std::exception& e) {
1343 SG_LOG(SG_GENERAL, SG_INFO, "caught " << e.what() << " << shutting down:" << nm);
1344 } catch (...) {
1345 SG_LOG(SG_GENERAL, SG_INFO, "caught generic exception shutting down:" << nm);
1346 }
1347
1348 // don't delete here, dropping the ref should be sufficient
1349 }
1350 } // of top-level groups iteration
1351
1352 // drop any references to AI objects with TACAN
1354
1355 // needed or we crash in multi-threaded OSG mode
1356 render->getViewerBase()->stopThreading();
1357
1358 osg::ref_ptr<osgViewer::CompositeViewer> composite_viewer = render->getCompositeViewer();
1359 osg::ref_ptr<osgViewer::View> composite_viewer_view;
1360 if (composite_viewer) {
1361 composite_viewer_view = render->getView();
1362 }
1363
1364 // order is important here since tile-manager shutdown needs to
1365 // access the scenery object
1366 subsystemManger->remove(FGScenery::staticSubsystemClassId());
1367
1370
1371
1372 osgDB::Registry::instance()->clearObjectCache();
1373 // Pager requests depend on this, so don't clear it until now
1374 sgUserDataInit( NULL );
1375
1376 // preserve the event handler; re-creating it would entail fixing the
1377 // idle handler
1378 osg::ref_ptr<flightgear::FGEventHandler> eventHandler = render->getEventHandler();
1379 // tell the event handler to drop properties, etc
1380 eventHandler->clear();
1381
1382 globals->set_renderer(NULL);
1383 globals->set_matlib(NULL);
1384
1387
1388 simgear::clearEffectCache();
1389 simgear::VPBTechnique::clearConstraints();
1390 simgear::SGModelLib::resetPropertyRoot();
1391 simgear::ParticlesGlobalManager::clear();
1392 simgear::UniformFactory::instance()->reset();
1393
1395
1396 globals->resetPropertyRoot();
1397 // otherwise channels are duplicated
1398 globals->get_channel_options_list()->clear();
1399
1400 // IMPORTANT
1401 // this is the low-water mark of the reset process.
1402 // Subsystems (except the special ones), properties, OSG nodes, Effects
1403 // should all be gone at this, except for special things, such as the
1404 // splash node.
1405 // From here onwards we're recreating early parts of main/init, before
1406 // we restart the main loop.
1407 // This is the place to check that instances of classes have all be
1408 // cleaned up correctly. (Also, the OSG threads are all paused, we're back
1409 // in single threaded mode)
1411
1413
1414 fgInitConfig(0, NULL, true);
1415 fgInitGeneral(); // all of this?
1416
1417 // set out new property root on the command manager
1418 SGCommandMgr::instance()->setImplicitRoot(globals->get_props());
1419
1421
1422 // Rebuild the lists of allowed paths for cases where a path comes from an
1423 // untrusted source, such as the global property tree (this uses $FG_HOME
1424 // and other paths set by Options::processOptions()).
1426
1427 const auto& resMgr = simgear::EmbeddedResourceManager::instance();
1428 // The language was (re)set in processOptions()
1429 const string locale = globals->get_locale()->getPreferredLanguage();
1430 resMgr->selectLocale(locale);
1431 SG_LOG(SG_GENERAL, SG_INFO,
1432 "EmbeddedResourceManager: selected locale '" << locale << "'");
1433
1434 // PRESERVED properties over-write state from options, intentionally
1435 if ( copyProperties(preserved, globals->get_props()) ) {
1436 SG_LOG( SG_GENERAL, SG_INFO, "Preserved state restored successfully" );
1437 } else {
1438 SG_LOG( SG_GENERAL, SG_INFO,
1439 "Some errors restoring preserved state (read-only props?)" );
1440 }
1441
1442 fgInitAircraftPaths(true);
1443 fgInitAircraft(true, false /* not from launcher */);
1444
1445 globals->get_locale()->loadAircraftTranslations();
1446 globals->get_locale()->loadAddonTranslations();
1447
1448 auto presets = globals->get_subsystem_mgr()->add<flightgear::GraphicsPresets>();
1449 presets->applyInitialPreset();
1450
1451 render = new FGRenderer;
1452 render->setCompositeViewer(composite_viewer);
1453 render->setEventHandler(eventHandler);
1454 eventHandler->reset();
1455 globals->set_renderer(render);
1456 render->init();
1457 render->setView(composite_viewer_view);
1458
1459 unsigned int numDBPagerThreads = std::max(fgGetNode("/sim/rendering/database-pager/threads", true)->getIntValue(1), 1);
1460 composite_viewer_view->setDatabasePager(FGScenery::getPagerSingleton());
1461 composite_viewer_view->getDatabasePager()->setUnrefImageDataAfterApplyPolicy(true, false);
1462 composite_viewer_view->getDatabasePager()->setUpThreads(numDBPagerThreads, 0);
1463 composite_viewer_view->getDatabasePager()->setAcceptNewDatabaseRequests(true);
1464 composite_viewer_view->setFrameStamp(composite_viewer->getFrameStamp());
1465
1466 flightgear::CameraGroup::buildDefaultGroup(composite_viewer_view);
1467 osg::GraphicsContext::createNewContextID();
1468
1469 render->setView(composite_viewer_view);
1470 render->postinit();
1471 composite_viewer->startThreading();
1472
1474
1475// init some things manually
1476// which do not follow the regular init pattern
1477
1478 globals->get_event_mgr()->init();
1479 globals->get_event_mgr()->setRealtimeProperty(fgGetNode("/sim/time/delta-realtime-sec", true));
1480
1481 globals->set_matlib( new SGMaterialLib );
1482
1483// terra-sync needs the property tree root, pass it back in
1484 auto terra_sync = subsystemManger->get_subsystem<simgear::SGTerraSync>();
1485 if (terra_sync) {
1486 terra_sync->setRoot(globals->get_props());
1487 }
1488
1489 fgSetBool("/sim/signals/reinit", false);
1490 fgSetBool("/sim/freeze/master", false);
1491 fgSetBool("/sim/sceneryloaded",false);
1492
1493 flightgear::addSentryBreadcrumb("end of reset", "info");
1494}
1495
1497{
1498 if (globals->packageRoot()) {
1499 return;
1500 }
1501
1502 SGPath packageAircraftDir = flightgear::Options::sharedInstance()->actualDownloadDir();
1503 packageAircraftDir.append("Aircraft");
1504
1505 SG_LOG(SG_GENERAL, SG_INFO, "init package root at:" << packageAircraftDir);
1506
1507 SGSharedPtr<Root> pkgRoot(new Root(packageAircraftDir, FLIGHTGEAR_VERSION));
1508 // set the http client later (too early in startup right now)
1509 globals->setPackageRoot(pkgRoot);
1510
1511}
1512
1514{
1515 SGPath dataPath = SGPath::fromEnv("FG_HOME", platformDefaultDataPath());
1516 simgear::Dir fgHome(dataPath);
1517 if (fgHome.exists()) {
1518 if (!fgHome.remove(true /* recursive */)) {
1519 fprintf(stderr, "Errors occurred trying to remove FG_HOME");
1520 return EXIT_FAILURE;
1521 }
1522 }
1523
1524 if (fgHome.exists()) {
1525 fprintf(stderr, "unable to remove FG_HOME");
1526 return EXIT_FAILURE;
1527 }
1528
1529#if defined(SG_WINDOWS)
1531 // we don't want to remove the whole dir, let's nuke specific
1532 // subdirs which are (hopefully) safe
1533
1534 SGPath terrasyncPath = p / "TerraSync";
1535 if (terrasyncPath.exists()) {
1536 simgear::Dir dir(terrasyncPath);
1537 if (!dir.remove(true /*recursive*/)) {
1538 std::cerr << "Errors occurred trying to remove " << terrasyncPath << std::endl;
1539 }
1540 }
1541
1542 SGPath packagesPath = p / "Aircraft";
1543 if (packagesPath.exists()) {
1544 simgear::Dir dir(packagesPath);
1545 if (!dir.remove(true /*recursive*/)) {
1546 std::cerr << "Errors occurred trying to remove " << packagesPath << std::endl;
1547 }
1548 }
1549
1550 SGPath cachePath = p / "TextureCache";
1551 if (cachePath.exists()) {
1552 simgear::Dir dir(cachePath);
1553 if (!dir.remove(true /*recursive*/)) {
1554 std::cerr << "Errors occurred trying to remove " << cachePath << std::endl;
1555 }
1556 }
1557#endif
1558 return EXIT_SUCCESS;
1559}
#define p(x)
bool options(int, char **)
Definition JSBSim.cpp:568
#define i(x)
Wrap SGEphemeris in a subsystem/property interface.
Definition ephemeris.hxx:37
Wrap an FDM implementation in a subsystem with standard semantics Notably, deal with the various case...
Definition fdm_shell.hxx:44
Manage environment information.
record the history of the aircraft's movements, making it available as a contiguous block.
SGPropertyNode * get_props()
Definition globals.hxx:320
const SGPath & get_fg_root() const
Definition globals.hxx:189
Definition fg_io.hxx:21
Generic input module.
Definition input.hxx:46
Manage aircraft instruments.
Log any property values to any number of CSV files.
Definition logger.hxx:21
Manage a list of user-specified models.
Definition modelmgr.hxx:26
osgViewer::View * getView()
Definition renderer.cxx:807
osg::ref_ptr< osgViewer::CompositeViewer > getCompositeViewer()
Both should only be used on reset.
Definition renderer.cxx:788
void setView(osgViewer::View *view)
Definition renderer.cxx:827
flightgear::FGEventHandler * getEventHandler()
Definition renderer.cxx:838
void init()
Initialize the renderer.
Definition renderer.cxx:233
osgViewer::ViewerBase * getViewerBase() const
Definition renderer.cxx:782
void setEventHandler(flightgear::FGEventHandler *event_handler)
Definition renderer.cxx:850
void postinit()
Called after init() was called, the graphics window has been created and the CameraGroup has been ini...
Definition renderer.cxx:337
void setCompositeViewer(osg::ref_ptr< osgViewer::CompositeViewer > composite_viewer)
Definition renderer.cxx:794
Top level route manager class.
Definition route_mgr.hxx:27
static flightgear::SceneryPager * getPagerSingleton()
Definition scenery.cxx:560
static const char * staticSubsystemClassId()
Definition scenery.hxx:88
Manage aircraft systems.
static FGXMLAutopilotGroup * createInstance(const std::string &nodeName)
bool haveExplicitAircraft() const
haveExplicitAircraft - check if the combination of /sim/aircraft and /sim/aircraft-dir defines an exp...
Definition fg_init.cxx:216
FindAndCacheAircraft(SGPropertyNode *autoSave)
Definition fg_init.cxx:200
void setDidUseLauncher(bool didUseLauncher)
Definition fg_init.cxx:205
XML-configured GUI subsystem.
Definition new_gui.hxx:31
Registry for performance data.
static CameraGroup * getDefault()
Get the default CameraGroup.
static void buildDefaultGroup(osgViewer::View *view)
Set the default CameraGroup, which is the only one that matters at this time.
static void setDefault(CameraGroup *group)
static const char * staticSubsystemClassId()
void applyInitialPreset()
init() is called too late (after fgOSInit), so we call this method early, to load the initial preset ...
static NavDataCache * createInstance()
unsigned int rebuildPhaseCompletionPercentage() const
static NavDataCache * instance()
bool isRebuildRequired()
predicate - check if the cache needs to be rebuilt.
RebuildPhase rebuild()
run the cache rebuild - returns the current phase or 'done'
bool loadTacan(const SGPath &path, FGTACANList *channellist)
Definition navdb.cxx:500
OptionResult processOptions()
apply option values to the simulation state (set properties, etc).
Definition options.cxx:3025
void initPaths()
process command line options relating to scenery / aircraft / data paths
Definition options.cxx:2569
SGPath actualDownloadDir()
the actual download dir in use, which may be the default or a user-supplied value
Definition options.cxx:3010
OptionResult initAircraft()
init the aircraft options
Definition options.cxx:2583
static Options * sharedInstance()
Definition options.cxx:2345
static const std::unique_ptr< AddonManager > & createInstance()
static FGHttpd * createInstance(SGPropertyNode_ptr configNode)
Definition httpd.cxx:641
const char * name
int fgUninstall()
Definition fg_init.cxx:1513
void fgInitPackageRoot()
Definition fg_init.cxx:1496
void fgPostInitSubsystems()
Definition fg_init.cxx:1171
bool fgInitNav()
Initialize vor/ndb/ils/fix list management and query systems (as well as simple airport db list) This...
Definition fg_init.cxx:892
int fgInitAircraft(bool reinit, bool didUseLauncher)
Definition fg_init.cxx:803
void fgShutdownHome()
Definition fg_init.cxx:637
static SGPath platformDefaultDataPath()
Definition fg_init.cxx:514
int fgInitConfig(int argc, char **argv, bool reinit)
Definition fg_init.cxx:688
void fgCreateSubsystems(bool duringReset)
Definition fg_init.cxx:997
static void initAircraftDirsNasalSecurity()
Definition fg_init.cxx:770
void fgStartNewReset()
Definition fg_init.cxx:1283
bool fgInitGeneral()
Definition fg_init.cxx:937
string fgBasePackageVersion(const SGPath &base_path)
Definition fg_init.cxx:156
void fgInitAircraftPaths(bool reinit)
Definition fg_init.cxx:788
static void createBaseStorageDirForAddons(const SGPath &exportDir)
Definition fg_init.cxx:661
void fgStartReposition()
Definition fg_init.cxx:1204
void fgDeleteLockFile()
Definition fg_init.cxx:651
std::optional< FGBasePackageInfo > fgBasePackageInfo(const SGPath &path)
Parse the base package info JSON.
Definition fg_init.cxx:174
SGPath fgHomePath()
Definition fg_init.cxx:524
InitHomeResult fgInitHome()
Definition fg_init.cxx:529
void fgOutputSettings()
Definition fg_init.cxx:972
InitHomeResult
Definition fg_init.hxx:42
@ InitHomeAbort
Definition fg_init.hxx:46
@ InitHomeExplicitReadOnly
Definition fg_init.hxx:45
@ InitHomeReadOnly
Definition fg_init.hxx:44
@ InitHomeOkay
Definition fg_init.hxx:43
void fgOSResetProperties()
void fgAddChangeListener(SGPropertyChangeListener *listener, const char *path)
Add a listener to a node.
Definition fg_props.cxx:513
bool fgLoadProps(const std::string &path, SGPropertyNode *props, bool in_fg_root, int default_mode)
Load properties from a file.
Definition fg_props.cxx:469
std::string fgGetString(const char *name, const char *defaultValue)
Get a string value for a property.
Definition fg_props.cxx:556
void fgSetArchivable(const char *name, bool state)
Set the state of the archive attribute for a property.
Definition fg_props.cxx:598
FGGlobals * globals
Definition globals.cxx:142
std::vector< SGPath > PathList
Definition globals.hxx:37
FlightGear Localization Support.
const std::string & getStringValue(const char *spec)
const char * PROPERTY_ROOT
Definition httpd.cxx:49
MessageBoxResult modalMessageBox(const std::string &caption, const std::string &msg, const std::string &moreText)
MessageBoxResult fatalMessageBoxWithoutExit(const std::string &caption, const std::string &msg, const std::string &moreText, bool reportToSentry)
SGPath defaultDownloadDir()
return the default platform dependant download directory.
Definition options.cxx:3001
void applyInitialState()
void unregisterMainLoopProperties()
Definition main.cxx:274
void updateSentryTag(const std::string &, const std::string &)
void addSentryBreadcrumb(const std::string &, const std::string &)
osg::Camera * getGUICamera(CameraGroup *cgroup)
Get the osg::Camera that draws the GUI, if any, from a camera group.
void addSentryTag(const char *, const char *)
void fatalMessageBoxThenExit(const std::string &caption, const std::string &msg, const std::string &moreText, int exitStatus, bool reportToSentry)
bool initPosition()
@ FG_OPTIONS_SHOW_AIRCRAFT
Definition options.hxx:58
@ FG_OPTIONS_OK
Definition options.hxx:53
@ FG_OPTIONS_ERROR
Definition options.hxx:55
void fgSetDefaults()
Set a few fail-safe default property values.
Definition options.cxx:127
bool fgGetBool(char const *name, bool def)
Get a bool value for a property.
Definition proptest.cpp:25
bool fgSetBool(char const *name, bool val)
Set a bool value for a property.
Definition proptest.cpp:24
bool fgSetString(char const *name, char const *str)
Set a string value for a property.
Definition proptest.cpp:26
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
Definition proptest.cpp:27
static osg::ref_ptr< SceneryPager > pager
Definition scenery.cxx:558
void fgSplashProgress(const char *identifier, unsigned int percent)
Set progress information.
Definition splash.cxx:863
structure holding parsed info from <base-package>/base_package.json
Definition fg_init.hxx:25
std::string buildDate
Definition fg_init.hxx:26
std::string gitRevision
Definition fg_init.hxx:27
static void resetStatisticsProperties()
Definition replay.cxx:119
virtual void valueChanged(SGPropertyNode *node)
Definition fg_init.cxx:681
void fgInitAllowedPaths()
Allowed paths here are absolute, and may contain one *, which matches any string.
Definition util.cxx:112