]> git.mxchange.org Git - flightgear.git/blob - src/Main/globals.cxx
Merge branch 'next' of gitorious.org:fg/flightgear into next
[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 SGPath FGGlobals::resolve_ressource_path(const std::string& branch) const
339 {
340   return simgear::ResourceManager::instance()
341     ->findPath(branch, SGPath(fgGetString("/sim/aircraft-dir")));
342 }
343
344 FGRenderer *
345 FGGlobals::get_renderer () const
346 {
347    return renderer;
348 }
349
350 SGSubsystemMgr *
351 FGGlobals::get_subsystem_mgr () const
352 {
353     return subsystem_mgr;
354 }
355
356 SGSubsystem *
357 FGGlobals::get_subsystem (const char * name)
358 {
359     return subsystem_mgr->get_subsystem(name);
360 }
361
362 void
363 FGGlobals::add_subsystem (const char * name,
364                           SGSubsystem * subsystem,
365                           SGSubsystemMgr::GroupType type,
366                           double min_time_sec)
367 {
368     subsystem_mgr->add(name, subsystem, type, min_time_sec);
369 }
370
371 SGSoundMgr *
372 FGGlobals::get_soundmgr () const
373 {
374     if (subsystem_mgr)
375         return (SGSoundMgr*) subsystem_mgr->get_subsystem("sound");
376
377     return NULL;
378 }
379
380 SGEventMgr *
381 FGGlobals::get_event_mgr () const
382 {
383     return event_mgr;
384 }
385
386 const SGGeod &
387 FGGlobals::get_aircraft_position() const
388 {
389     if( acmodel != NULL ) {
390         SGModelPlacement * mp = acmodel->get3DModel();
391         if( mp != NULL )
392             return mp->getPosition();
393     }
394     throw sg_exception("Can't get aircraft position", "FGGlobals::get_aircraft_position()" );
395 }
396
397 SGVec3d
398 FGGlobals::get_aircraft_positon_cart() const
399 {
400     return SGVec3d::fromGeod(get_aircraft_position());
401 }
402
403
404 // Save the current state as the initial state.
405 void
406 FGGlobals::saveInitialState ()
407 {
408   initial_state = new SGPropertyNode();
409
410   // copy properties which are READ/WRITEable - but not USERARCHIVEd or PRESERVEd
411   int checked  = SGPropertyNode::READ+SGPropertyNode::WRITE+
412                  SGPropertyNode::USERARCHIVE+SGPropertyNode::PRESERVE;
413   int expected = SGPropertyNode::READ+SGPropertyNode::WRITE;
414   if (!copyProperties(props, initial_state, expected, checked))
415     SG_LOG(SG_GENERAL, SG_ALERT, "Error saving initial state");
416     
417   // delete various properties from the initial state, since we want to
418   // preserve their values even if doing a restore
419   // => Properties should now use the PRESERVE flag to protect their values
420   // on sim-reset. Remove some specific properties for backward compatibility.
421   SGPropertyNode* sim = initial_state->getChild("sim");
422   SGPropertyNode* cameraGroupNode = sim->getNode("rendering/camera-group");
423   if (cameraGroupNode) {
424     cameraGroupNode->removeChild("camera");
425     cameraGroupNode->removeChild("gui");
426   }
427 }
428
429
430 // Restore the saved initial state, if any
431 void
432 FGGlobals::restoreInitialState ()
433 {
434     if ( initial_state == 0 ) {
435         SG_LOG(SG_GENERAL, SG_ALERT,
436                "No initial state available to restore!!!");
437         return;
438     }
439     // copy properties which are READ/WRITEable - but not USERARCHIVEd or PRESERVEd
440     int checked  = SGPropertyNode::READ+SGPropertyNode::WRITE+
441                    SGPropertyNode::USERARCHIVE+SGPropertyNode::PRESERVE;
442     int expected = SGPropertyNode::READ+SGPropertyNode::WRITE;
443     if ( copyProperties(initial_state, props, expected, checked)) {
444         SG_LOG( SG_GENERAL, SG_INFO, "Initial state restored successfully" );
445     } else {
446         SG_LOG( SG_GENERAL, SG_INFO,
447                 "Some errors restoring initial state (read-only props?)" );
448     }
449
450 }
451
452 // Load user settings from autosave.xml
453 void
454 FGGlobals::loadUserSettings(const SGPath& dataPath)
455 {
456     // remember that we have (tried) to load any existing autsave.xml
457     haveUserSettings = true;
458
459     SGPath autosaveFile = simgear::Dir(dataPath).file("autosave.xml");
460     SGPropertyNode autosave;
461     if (autosaveFile.exists()) {
462       SG_LOG(SG_INPUT, SG_INFO, "Reading user settings from " << autosaveFile.str());
463       try {
464           readProperties(autosaveFile.str(), &autosave, SGPropertyNode::USERARCHIVE);
465       } catch (sg_exception& e) {
466           SG_LOG(SG_INPUT, SG_WARN, "failed to read user settings:" << e.getMessage()
467             << "(from " << e.getOrigin() << ")");
468       }
469     }
470     copyProperties(&autosave, globals->get_props());
471 }
472
473 // Save user settings in autosave.xml
474 void
475 FGGlobals::saveUserSettings()
476 {
477     // only save settings when we have (tried) to load the previous
478     // settings (otherwise user data was lost)
479     if (!haveUserSettings)
480         return;
481
482     if (fgGetBool("/sim/startup/save-on-exit")) {
483       // don't save settings more than once on shutdown
484       haveUserSettings = false;
485
486       SGPath autosaveFile(fgGetString("/sim/fg-home"));
487       autosaveFile.append( "autosave.xml" );
488       autosaveFile.create_dir( 0700 );
489       SG_LOG(SG_IO, SG_INFO, "Saving user settings to " << autosaveFile.str());
490       try {
491         writeProperties(autosaveFile.str(), globals->get_props(), false, SGPropertyNode::USERARCHIVE);
492       } catch (const sg_exception &e) {
493         guiErrorMessage("Error writing autosave.xml: ", e);
494       }
495       SG_LOG(SG_INPUT, SG_DEBUG, "Finished Saving user settings");
496     }
497 }
498
499 FGViewer *
500 FGGlobals::get_current_view () const
501 {
502   return viewmgr->get_current_view();
503 }
504
505 long int FGGlobals::get_warp() const
506 {
507   return fgGetInt("/sim/time/warp");
508 }
509
510 void FGGlobals::set_warp( long int w )
511 {
512   fgSetInt("/sim/time/warp", w);
513 }
514
515 long int FGGlobals::get_warp_delta() const
516 {
517   return fgGetInt("/sim/time/warp-delta");
518 }
519
520 void FGGlobals::set_warp_delta( long int d )
521 {
522   fgSetInt("/sim/time/warp-delta", d);
523 }
524
525 // end of globals.cxx