]> git.mxchange.org Git - flightgear.git/blob - src/Main/globals.cxx
c63129866c95da9758c0e788f66e8d12f70b608a
[flightgear.git] / src / Main / globals.cxx
1 // globals.cxx -- Global state that needs to be shared among the sim modules
2 //
3 // Written by Curtis Olson, started July 2000.
4 //
5 // Copyright (C) 2000  Curtis L. Olson - http://www.flightgear.org/~curt
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software Foundation,
19 // Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 // $Id$
22
23 #ifdef HAVE_CONFIG_H
24 #  include <config.h>
25 #endif
26
27 #include <boost/foreach.hpp>
28 #include <algorithm>
29
30 #include <simgear/structure/commands.hxx>
31 #include <simgear/misc/sg_path.hxx>
32 #include <simgear/misc/sg_dir.hxx>
33 #include <simgear/timing/sg_time.hxx>
34 #include <simgear/ephemeris/ephemeris.hxx>
35 #include <simgear/scene/material/matlib.hxx>
36 #include <simgear/structure/subsystem_mgr.hxx>
37 #include <simgear/structure/event_mgr.hxx>
38 #include <simgear/sound/soundmgr_openal.hxx>
39 #include <simgear/misc/ResourceManager.hxx>
40 #include <simgear/props/propertyObject.hxx>
41 #include <simgear/props/props_io.hxx>
42 #include <simgear/scene/model/placement.hxx>
43
44 #include <Aircraft/controls.hxx>
45 #include <Airports/runways.hxx>
46 #include <ATCDCL/ATISmgr.hxx>
47 #include <Autopilot/route_mgr.hxx>
48 #include <GUI/FGFontCache.hxx>
49 #include <GUI/gui.h>
50 #include <Model/acmodel.hxx>
51 #include <Model/modelmgr.hxx>
52 #include <MultiPlayer/multiplaymgr.hxx>
53 #include <Scenery/scenery.hxx>
54 #include <Scenery/tilemgr.hxx>
55 #include <Navaids/navlist.hxx>
56 #include <Viewer/renderer.hxx>
57 #include <Viewer/viewmgr.hxx>
58
59 #include "globals.hxx"
60 #include "locale.hxx"
61
62 #include "fg_props.hxx"
63 #include "fg_io.hxx"
64
65 class AircraftResourceProvider : public simgear::ResourceProvider
66 {
67 public:
68   AircraftResourceProvider() :
69     simgear::ResourceProvider(simgear::ResourceManager::PRIORITY_HIGH)
70   {
71   }
72   
73   virtual SGPath resolve(const std::string& aResource, SGPath&) const
74   {
75     string_list pieces(sgPathBranchSplit(aResource));
76     if ((pieces.size() < 3) || (pieces.front() != "Aircraft")) {
77       return SGPath(); // not an Aircraft path
78     }
79     
80   // test against the aircraft-dir property
81     const char* aircraftDir = fgGetString("/sim/aircraft-dir");
82     string_list aircraftDirPieces(sgPathBranchSplit(aircraftDir));
83     if (!aircraftDirPieces.empty() && (aircraftDirPieces.back() == pieces[1])) {
84         // current aircraft-dir matches resource aircraft
85         SGPath r(aircraftDir);
86         for (unsigned int i=2; i<pieces.size(); ++i) {
87           r.append(pieces[i]);
88         }
89         
90         if (r.exists()) {
91           SG_LOG(SG_IO, SG_DEBUG, "found path:" << aResource << " via /sim/aircraft-dir: " << r.str());
92           return r;
93         }
94     }
95   
96   // try each aircraft dir in turn
97     std::string res(aResource, 9); // resource path with 'Aircraft/' removed
98     const string_list& dirs(globals->get_aircraft_paths());
99     string_list::const_iterator it = dirs.begin();
100     for (; it != dirs.end(); ++it) {
101       SGPath p(*it, res);
102       if (p.exists()) {
103         SG_LOG(SG_IO, SG_DEBUG, "found path:" << aResource << " in aircraft dir: " << *it);
104         return p;
105       }
106     } // of aircraft path iteration
107     
108     return SGPath(); // not found
109   }
110 };
111
112 ////////////////////////////////////////////////////////////////////////
113 // Implementation of FGGlobals.
114 ////////////////////////////////////////////////////////////////////////
115
116 // global global :-)
117 FGGlobals *globals;
118
119
120 // Constructor
121 FGGlobals::FGGlobals() :
122     props( new SGPropertyNode ),
123     initial_state( NULL ),
124     locale( new FGLocale(props) ),
125     renderer( new FGRenderer ),
126     subsystem_mgr( new SGSubsystemMgr ),
127     event_mgr( new SGEventMgr ),
128     sim_time_sec( 0.0 ),
129     fg_root( "" ),
130     fg_home( "" ),
131     time_params( NULL ),
132     ephem( NULL ),
133     matlib( NULL ),
134     route_mgr( NULL ),
135     ATIS_mgr( NULL ),
136     controls( NULL ),
137     viewmgr( NULL ),
138     commands( SGCommandMgr::instance() ),
139     acmodel( NULL ),
140     model_mgr( NULL ),
141     channel_options_list( NULL ),
142     initial_waypoints( NULL ),
143     scenery( NULL ),
144     tile_mgr( NULL ),
145     fontcache ( new FGFontCache ),
146     navlist( NULL ),
147     loclist( NULL ),
148     gslist( NULL ),
149     dmelist( NULL ),
150     tacanlist( NULL ),
151     carrierlist( NULL ),
152     channellist( NULL ),
153     haveUserSettings(false)
154 {
155   simgear::ResourceManager::instance()->addProvider(new AircraftResourceProvider());
156   simgear::PropertyObjectBase::setDefaultRoot(props);
157 }
158
159 // Destructor
160 FGGlobals::~FGGlobals() 
161 {
162     // save user settings (unless already saved)
163     saveUserSettings();
164
165     // The AIModels manager performs a number of actions upon
166     // Shutdown that implicitly assume that other subsystems
167     // are still operational (Due to the dynamic allocation and
168     // deallocation of AIModel objects. To ensure we can safely
169     // shut down all subsystems, make sure we take down the 
170     // AIModels system first.
171     SGSubsystem* ai = subsystem_mgr->remove("ai-model");
172     if (ai) {
173         ai->unbind();
174         delete ai;
175     }
176     SGSubsystem* sound = subsystem_mgr->remove("sound");
177
178     subsystem_mgr->shutdown();
179     subsystem_mgr->unbind();
180     delete subsystem_mgr;
181     
182     delete renderer;
183     renderer = NULL;
184     
185     delete time_params;
186     delete matlib;
187     delete route_mgr;
188
189     delete ATIS_mgr;
190
191     if (controls)
192     {
193         controls->unbind();
194         delete controls;
195     }
196
197     delete channel_options_list;
198     delete initial_waypoints;
199     delete scenery;
200     delete fontcache;
201
202     delete navlist;
203     delete loclist;
204     delete gslist;
205     delete dmelist;
206     delete tacanlist;
207     delete carrierlist;
208     delete channellist;
209     delete sound;
210
211     delete locale;
212     locale = NULL;
213 }
214
215 // set the fg_root path
216 void FGGlobals::set_fg_root (const string &root) {
217     SGPath tmp(root);
218     fg_root = tmp.realpath();
219
220     // append /data to root if it exists
221     tmp.append( "data" );
222     tmp.append( "version" );
223     if ( tmp.exists() ) {
224         fgGetNode("BAD_FG_ROOT", true)->setStringValue(fg_root);
225         fg_root += "/data";
226         fgGetNode("GOOD_FG_ROOT", true)->setStringValue(fg_root);
227         SG_LOG(SG_GENERAL, SG_ALERT, "***\n***\n*** Warning: changing bad FG_ROOT/--fg-root to '"
228                 << fg_root << "'\n***\n***");
229     }
230
231     // remove /sim/fg-root before writing to prevent hijacking
232     SGPropertyNode *n = fgGetNode("/sim", true);
233     n->removeChild("fg-root", 0, false);
234     n = n->getChild("fg-root", 0, true);
235     n->setStringValue(fg_root.c_str());
236     n->setAttribute(SGPropertyNode::WRITE, false);
237     
238     simgear::ResourceManager::instance()->addBasePath(fg_root, 
239       simgear::ResourceManager::PRIORITY_DEFAULT);
240 }
241
242 // set the fg_home path
243 void FGGlobals::set_fg_home (const string &home) {
244     SGPath tmp(home);
245     fg_home = tmp.realpath();
246 }
247
248 void FGGlobals::append_fg_scenery (const string &paths)
249 {
250 //    fg_scenery.clear();
251     SGPropertyNode* sim = fgGetNode("/sim", true);
252
253   // find first unused fg-scenery property in /sim
254     int propIndex = 0;
255     while (sim->getChild("fg-scenery", propIndex) != NULL) {
256       ++propIndex; 
257     }
258   
259     BOOST_FOREACH(const SGPath& path, sgPathSplit( paths )) {
260         SGPath abspath(path.realpath());
261         if (!abspath.exists()) {
262           SG_LOG(SG_GENERAL, SG_WARN, "scenery path not found:" << abspath.str());
263           continue;
264         }
265
266       // check for duplicates
267       string_list::const_iterator ex = std::find(fg_scenery.begin(), fg_scenery.end(), abspath.str());
268       if (ex != fg_scenery.end()) {
269         SG_LOG(SG_GENERAL, SG_INFO, "skipping duplicate add of scenery path:" << abspath.str());
270         continue;
271       }
272       
273         simgear::Dir dir(abspath);
274         SGPath terrainDir(dir.file("Terrain"));
275         SGPath objectsDir(dir.file("Objects"));
276         
277       // this code used to add *either* the base dir, OR add the 
278       // Terrain and Objects subdirs, but the conditional logic was commented
279       // out, such that all three dirs are added. Unfortunately there's
280       // no information as to why the change was made.
281         fg_scenery.push_back(abspath.str());
282         
283         if (terrainDir.exists()) {
284           fg_scenery.push_back(terrainDir.str());
285         }
286         
287         if (objectsDir.exists()) {
288           fg_scenery.push_back(objectsDir.str());
289         }
290         
291         // insert a marker for FGTileEntry::load(), so that
292         // FG_SCENERY=A:B becomes list ["A/Terrain", "A/Objects", "",
293         // "B/Terrain", "B/Objects", ""]
294         fg_scenery.push_back("");
295         
296       // make scenery dirs available to Nasal
297         SGPropertyNode* n = sim->getChild("fg-scenery", propIndex++, true);
298         n->setStringValue(abspath.str());
299         n->setAttribute(SGPropertyNode::WRITE, false);
300     } // of path list iteration
301 }
302
303 void FGGlobals::append_aircraft_path(const std::string& path)
304 {
305   SGPath dirPath(path);
306   if (!dirPath.exists()) {
307     SG_LOG(SG_GENERAL, SG_WARN, "aircraft path not found:" << path);
308     return;
309   }
310   std::string abspath = dirPath.realpath();
311   
312   unsigned int index = fg_aircraft_dirs.size();  
313   fg_aircraft_dirs.push_back(abspath);
314   
315 // make aircraft dirs available to Nasal
316   SGPropertyNode* sim = fgGetNode("/sim", true);
317   sim->removeChild("fg-aircraft", index, false);
318   SGPropertyNode* n = sim->getChild("fg-aircraft", index, true);
319   n->setStringValue(abspath);
320   n->setAttribute(SGPropertyNode::WRITE, false);
321 }
322
323 void FGGlobals::append_aircraft_paths(const std::string& path)
324 {
325   string_list paths = sgPathSplit(path);
326   for (unsigned int p = 0; p<paths.size(); ++p) {
327     append_aircraft_path(paths[p]);
328   }
329 }
330
331 SGPath FGGlobals::resolve_aircraft_path(const std::string& branch) const
332 {
333   return simgear::ResourceManager::instance()->findPath(branch);
334 }
335
336 SGPath FGGlobals::resolve_maybe_aircraft_path(const std::string& branch) const
337 {
338   return simgear::ResourceManager::instance()->findPath(branch);
339 }
340
341 SGPath FGGlobals::resolve_ressource_path(const std::string& branch) const
342 {
343   return simgear::ResourceManager::instance()
344     ->findPath(branch, SGPath(fgGetString("/sim/aircraft-dir")));
345 }
346
347 FGRenderer *
348 FGGlobals::get_renderer () const
349 {
350    return renderer;
351 }
352
353 SGSubsystemMgr *
354 FGGlobals::get_subsystem_mgr () const
355 {
356     return subsystem_mgr;
357 }
358
359 SGSubsystem *
360 FGGlobals::get_subsystem (const char * name)
361 {
362     return subsystem_mgr->get_subsystem(name);
363 }
364
365 void
366 FGGlobals::add_subsystem (const char * name,
367                           SGSubsystem * subsystem,
368                           SGSubsystemMgr::GroupType type,
369                           double min_time_sec)
370 {
371     subsystem_mgr->add(name, subsystem, type, min_time_sec);
372 }
373
374 SGSoundMgr *
375 FGGlobals::get_soundmgr () const
376 {
377     if (subsystem_mgr)
378         return (SGSoundMgr*) subsystem_mgr->get_subsystem("sound");
379
380     return NULL;
381 }
382
383 SGEventMgr *
384 FGGlobals::get_event_mgr () const
385 {
386     return event_mgr;
387 }
388
389 SGGeod
390 FGGlobals::get_aircraft_position() const
391 {
392   if( acmodel != NULL ) {
393       SGModelPlacement * mp = acmodel->get3DModel();
394       if( mp != NULL )
395           return mp->getPosition();
396   }
397
398   // fall back to reading the property tree. this can occur during
399   // startup before the acmodel is initialised
400   
401   return SGGeod::fromDegFt(fgGetDouble("/position/longitude-deg"),
402                            fgGetDouble("/position/latitude-deg"),
403                            fgGetDouble("/position/altitude-ft"));
404 }
405
406 SGVec3d
407 FGGlobals::get_aircraft_positon_cart() const
408 {
409     return SGVec3d::fromGeod(get_aircraft_position());
410 }
411
412
413 // Save the current state as the initial state.
414 void
415 FGGlobals::saveInitialState ()
416 {
417   initial_state = new SGPropertyNode();
418
419   // copy properties which are READ/WRITEable - but not USERARCHIVEd or PRESERVEd
420   int checked  = SGPropertyNode::READ+SGPropertyNode::WRITE+
421                  SGPropertyNode::USERARCHIVE+SGPropertyNode::PRESERVE;
422   int expected = SGPropertyNode::READ+SGPropertyNode::WRITE;
423   if (!copyProperties(props, initial_state, expected, checked))
424     SG_LOG(SG_GENERAL, SG_ALERT, "Error saving initial state");
425     
426   // delete various properties from the initial state, since we want to
427   // preserve their values even if doing a restore
428   // => Properties should now use the PRESERVE flag to protect their values
429   // on sim-reset. Remove some specific properties for backward compatibility.
430   SGPropertyNode* sim = initial_state->getChild("sim");
431   SGPropertyNode* cameraGroupNode = sim->getNode("rendering/camera-group");
432   if (cameraGroupNode) {
433     cameraGroupNode->removeChild("camera");
434     cameraGroupNode->removeChild("gui");
435   }
436 }
437
438
439 // Restore the saved initial state, if any
440 void
441 FGGlobals::restoreInitialState ()
442 {
443     if ( initial_state == 0 ) {
444         SG_LOG(SG_GENERAL, SG_ALERT,
445                "No initial state available to restore!!!");
446         return;
447     }
448     // copy properties which are READ/WRITEable - but not USERARCHIVEd or PRESERVEd
449     int checked  = SGPropertyNode::READ+SGPropertyNode::WRITE+
450                    SGPropertyNode::USERARCHIVE+SGPropertyNode::PRESERVE;
451     int expected = SGPropertyNode::READ+SGPropertyNode::WRITE;
452     if ( copyProperties(initial_state, props, expected, checked)) {
453         SG_LOG( SG_GENERAL, SG_INFO, "Initial state restored successfully" );
454     } else {
455         SG_LOG( SG_GENERAL, SG_INFO,
456                 "Some errors restoring initial state (read-only props?)" );
457     }
458
459 }
460
461 // Load user settings from autosave.xml
462 void
463 FGGlobals::loadUserSettings(const SGPath& dataPath)
464 {
465     // remember that we have (tried) to load any existing autsave.xml
466     haveUserSettings = true;
467
468     SGPath autosaveFile = simgear::Dir(dataPath).file("autosave.xml");
469     SGPropertyNode autosave;
470     if (autosaveFile.exists()) {
471       SG_LOG(SG_INPUT, SG_INFO, "Reading user settings from " << autosaveFile.str());
472       try {
473           readProperties(autosaveFile.str(), &autosave, SGPropertyNode::USERARCHIVE);
474       } catch (sg_exception& e) {
475           SG_LOG(SG_INPUT, SG_WARN, "failed to read user settings:" << e.getMessage()
476             << "(from " << e.getOrigin() << ")");
477       }
478     }
479     copyProperties(&autosave, globals->get_props());
480 }
481
482 // Save user settings in autosave.xml
483 void
484 FGGlobals::saveUserSettings()
485 {
486     // only save settings when we have (tried) to load the previous
487     // settings (otherwise user data was lost)
488     if (!haveUserSettings)
489         return;
490
491     if (fgGetBool("/sim/startup/save-on-exit")) {
492       // don't save settings more than once on shutdown
493       haveUserSettings = false;
494
495       SGPath autosaveFile(globals->get_fg_home());
496       autosaveFile.append( "autosave.xml" );
497       autosaveFile.create_dir( 0700 );
498       SG_LOG(SG_IO, SG_INFO, "Saving user settings to " << autosaveFile.str());
499       try {
500         writeProperties(autosaveFile.str(), globals->get_props(), false, SGPropertyNode::USERARCHIVE);
501       } catch (const sg_exception &e) {
502         guiErrorMessage("Error writing autosave.xml: ", e);
503       }
504       SG_LOG(SG_INPUT, SG_DEBUG, "Finished Saving user settings");
505     }
506 }
507
508 FGViewer *
509 FGGlobals::get_current_view () const
510 {
511   return viewmgr->get_current_view();
512 }
513
514 long int FGGlobals::get_warp() const
515 {
516   return fgGetInt("/sim/time/warp");
517 }
518
519 void FGGlobals::set_warp( long int w )
520 {
521   fgSetInt("/sim/time/warp", w);
522 }
523
524 long int FGGlobals::get_warp_delta() const
525 {
526   return fgGetInt("/sim/time/warp-delta");
527 }
528
529 void FGGlobals::set_warp_delta( long int d )
530 {
531   fgSetInt("/sim/time/warp-delta", d);
532 }
533
534 // end of globals.cxx