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