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