Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/core7/src/libraries/core/Core.cc @ 10509

Last change on this file since 10509 was 10509, checked in by landauf, 9 years ago

moved static application paths (root, executable, modules) into new class named ApplicationPaths
moved configurable data paths (data, log, config) into new class named ConfigurablePaths
removed PathConfig

  • Property svn:eol-style set to native
File size: 15.7 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Fabian 'x3n' Landau
24 *      Reto Grieder
25 *   Co-authors:
26 *      ...
27 *
28 */
29
30/**
31@file
32@brief
33    Implementation of the Core singleton with its global variables (avoids boost include)
34*/
35
36#include "Core.h"
37
38#include <cassert>
39#include <cstdlib>
40#include <ctime>
41#include <fstream>
42#include <vector>
43
44#ifdef ORXONOX_PLATFORM_WINDOWS
45#  ifndef WIN32_LEAN_AND_MEAN
46#    define WIN32_LEAN_AND_MEAN
47#  endif
48#  include <windows.h>
49#  undef min
50#  undef max
51#endif
52
53#include "util/Clock.h"
54#include "util/Output.h"
55#include "util/Exception.h"
56#include "util/output/LogWriter.h"
57#include "util/output/OutputManager.h"
58#include "core/singleton/Scope.h"
59#include "core/singleton/ScopedSingletonIncludes.h"
60#include "util/SignalHandler.h"
61#include "ApplicationPaths.h"
62#include "ConfigurablePaths.h"
63#include "commandline/CommandLineIncludes.h"
64#include "config/ConfigFileManager.h"
65#include "DynLibManager.h"
66#include "GameMode.h"
67#include "GraphicsManager.h"
68#include "GUIManager.h"
69#include "class/Identifier.h"
70#include "Language.h"
71#include "Loader.h"
72#include "LuaState.h"
73#include "command/ConsoleCommandManager.h"
74#include "command/IOConsole.h"
75#include "command/TclBind.h"
76#include "command/TclThreadManager.h"
77#include "input/InputManager.h"
78#include "object/ObjectList.h"
79#include "module/ModuleInstance.h"
80#include "UpdateListener.h"
81
82namespace orxonox
83{
84    //! Static pointer to the singleton
85    Core* Core::singletonPtr_s  = 0;
86
87    SetCommandLineArgument(settingsFile, "orxonox.ini").information("THE configuration file");
88#if !defined(ORXONOX_PLATFORM_APPLE) && !defined(ORXONOX_USE_WINMAIN)
89    SetCommandLineSwitch(noIOConsole).information("Use this if you don't want to use the IOConsole (for instance for Lua debugging)");
90#endif
91
92#ifdef ORXONOX_PLATFORM_WINDOWS
93    SetCommandLineArgument(limitToCPU, 0).information("Limits the program to one CPU/core (1, 2, 3, etc.). Default is off = 0.");
94#endif
95
96    Core::Core(const std::string& cmdLine)
97        : applicationPaths_(NULL)
98        , configurablePaths_(NULL)
99        , dynLibManager_(NULL)
100        , signalHandler_(NULL)
101        , configFileManager_(NULL)
102        , languageInstance_(NULL)
103        , loaderInstance_(NULL)
104        , ioConsole_(NULL)
105        , tclBind_(NULL)
106        , tclThreadManager_(NULL)
107        , rootScope_(NULL)
108        , graphicsManager_(NULL)
109        , inputManager_(NULL)
110        , guiManager_(NULL)
111        , graphicsScope_(NULL)
112        , bGraphicsLoaded_(false)
113        , config_(NULL)
114        , destructionHelper_(this)
115    {
116        orxout(internal_status) << "initializing Core object..." << endl;
117
118        // Set the hard coded fixed paths
119        this->applicationPaths_ = new ApplicationPaths();
120
121        // Create a new dynamic library manager
122        this->dynLibManager_ = new DynLibManager();
123
124        // Load modules
125        orxout(internal_info) << "Loading modules:" << endl;
126        const std::vector<std::string>& modulePaths = ApplicationPaths::getInstance().getModulePaths();
127        for (std::vector<std::string>::const_iterator it = modulePaths.begin(); it != modulePaths.end(); ++it)
128        {
129            try
130            {
131                this->dynLibManager_->load(*it);
132            }
133            catch (...)
134            {
135                orxout(user_error) << "Couldn't load module \"" << *it << "\": " << Exception::handleMessage() << endl;
136            }
137        }
138
139        // TODO: initialize Root-Context
140        // TODO: initialize IdentifierManager here
141        // TODO: initialize ScopeManager here
142        // TODO: initialize CommandLineParser here
143        // TODO: initialize ConsoleCommandManager here
144        ModuleInstance::getCurrentModuleInstance()->loadAllStaticallyInitializedInstances();
145
146        // Parse command line arguments AFTER the modules have been loaded (static code!)
147        CommandLineParser::parse(cmdLine);
148
149        // Set configurable paths like log, config and media
150        this->configurablePaths_ = new ConfigurablePaths();
151        this->configurablePaths_->setConfigurablePaths(ApplicationPaths::getInstance());
152
153        orxout(internal_info) << "Root path:       " << ApplicationPaths::getRootPathString() << endl;
154        orxout(internal_info) << "Executable path: " << ApplicationPaths::getExecutablePathString() << endl;
155        orxout(internal_info) << "Modules path:    " << ApplicationPaths::getModulePathString() << endl;
156
157        orxout(internal_info) << "Data path:       " << ConfigurablePaths::getDataPathString() << endl;
158        orxout(internal_info) << "Ext. data path:  " << ConfigurablePaths::getExternalDataPathString() << endl;
159        orxout(internal_info) << "Config path:     " << ConfigurablePaths::getConfigPathString() << endl;
160        orxout(internal_info) << "Log path:        " << ConfigurablePaths::getLogPathString() << endl;
161
162        // create a signal handler
163        // This call is placed as soon as possible, but after the directories are set
164        this->signalHandler_ = new SignalHandler();
165        this->signalHandler_->doCatch(ApplicationPaths::getExecutablePathString(), ConfigurablePaths::getLogPathString() + "orxonox_crash.log");
166
167#ifdef ORXONOX_PLATFORM_WINDOWS
168        // limit the main thread to the first core so that QueryPerformanceCounter doesn't jump
169        // do this after ogre has initialised. Somehow Ogre changes the settings again (not through
170        // the timer though).
171        int limitToCPU = CommandLineParser::getValue("limitToCPU");
172        if (limitToCPU > 0)
173            setThreadAffinity(static_cast<unsigned int>(limitToCPU));
174#endif
175
176        // Manage ini files and set the default settings file (usually orxonox.ini)
177        orxout(internal_info) << "Loading config:" << endl;
178        this->configFileManager_ = new ConfigFileManager();
179        this->configFileManager_->setFilename(ConfigFileType::Settings,
180            CommandLineParser::getValue("settingsFile").get<std::string>());
181
182        // Required as well for the config values
183        orxout(internal_info) << "Loading language:" << endl;
184        this->languageInstance_ = new Language();
185
186        // Do this soon after the ConfigFileManager has been created to open up the
187        // possibility to configure everything below here
188        orxout(internal_info) << "configuring Core" << endl;
189        this->config_ = new CoreConfig();
190
191        // Set the correct log path and rewrite the log file with the correct log levels
192        OutputManager::getInstance().getLogWriter()->setLogDirectory(ConfigurablePaths::getLogPathString());
193
194#if !defined(ORXONOX_PLATFORM_APPLE) && !defined(ORXONOX_USE_WINMAIN)
195        // Create persistent IO console
196        if (CommandLineParser::getValue("noIOConsole").get<bool>() == false && this->config_->getStartIOConsole())
197        {
198            orxout(internal_info) << "creating IO console" << endl;
199            this->ioConsole_ = new IOConsole();
200        }
201#endif
202
203        // creates the class hierarchy for all classes with factories
204        orxout(internal_info) << "creating class hierarchy" << endl;
205        IdentifierManager::getInstance().createClassHierarchy();
206
207        // Loader
208        this->loaderInstance_ = new Loader();
209
210        // Load OGRE excluding the renderer and the render window
211        orxout(internal_info) << "creating GraphicsManager:" << endl;
212        this->graphicsManager_ = new GraphicsManager(false);
213
214        // initialise Tcl
215        this->tclBind_ = new TclBind(ConfigurablePaths::getDataPathString());
216        this->tclThreadManager_ = new TclThreadManager(tclBind_->getTclInterpreter());
217
218        // Create singletons that always exist (in other libraries)
219        orxout(internal_info) << "creating root scope:" << endl;
220        this->rootScope_ = new Scope<ScopeID::ROOT>();
221
222        // Generate documentation instead of normal run?
223        std::string docFilename;
224        CommandLineParser::getValue("generateDoc", &docFilename);
225        if (!docFilename.empty())
226        {
227            std::ofstream docFile(docFilename.c_str());
228            if (docFile.is_open())
229            {
230                CommandLineParser::generateDoc(docFile);
231                docFile.close();
232            }
233            else
234                orxout(internal_error) << "Could not open file for documentation writing" << endl;
235        }
236
237        orxout(internal_status) << "finished initializing Core object" << endl;
238    }
239
240    void Core::destroy()
241    {
242        orxout(internal_status) << "destroying Core object..." << endl;
243
244        safeObjectDelete(&graphicsScope_);
245        safeObjectDelete(&guiManager_);
246        safeObjectDelete(&inputManager_);
247        safeObjectDelete(&graphicsManager_);
248        safeObjectDelete(&rootScope_);
249        safeObjectDelete(&tclThreadManager_);
250        safeObjectDelete(&tclBind_);
251        safeObjectDelete(&ioConsole_);
252        safeObjectDelete(&loaderInstance_);
253        safeObjectDelete(&config_);
254        safeObjectDelete(&languageInstance_);
255        safeObjectDelete(&configFileManager_);
256        ConsoleCommandManager::getInstance().destroyAll();
257        Context::setRootContext(NULL);
258        IdentifierManager::getInstance().destroyAllIdentifiers();
259        safeObjectDelete(&signalHandler_);
260        safeObjectDelete(&dynLibManager_);
261        safeObjectDelete(&configurablePaths_);
262        safeObjectDelete(&applicationPaths_);
263
264        orxout(internal_status) << "finished destroying Core object" << endl;
265    }
266
267    void Core::loadGraphics()
268    {
269        orxout(internal_info) << "loading graphics in Core" << endl;
270
271        // Any exception should trigger this, even in upgradeToGraphics (see its remarks)
272        Loki::ScopeGuard unloader = Loki::MakeObjGuard(*this, &Core::unloadGraphics);
273
274        // Upgrade OGRE to receive a render window
275        try
276        {
277            graphicsManager_->upgradeToGraphics();
278        }
279        catch (const InitialisationFailedException&)
280        {
281            // Exit the application if the Ogre config dialog was canceled
282            orxout(user_error) << Exception::handleMessage() << endl;
283            exit(EXIT_FAILURE);
284        }
285        catch (...)
286        {
287            // Recovery from this is very difficult. It requires to completely
288            // destroy Ogre related objects and load again (without graphics).
289            // However since Ogre 1.7 there seems to be a problem when Ogre
290            // throws an exception and the graphics engine then gets destroyed
291            // and reloaded between throw and catch (access violation in MSVC).
292            // That's why we abort completely and only display the exception.
293            orxout(user_error) << "An exception occurred during upgrade to graphics. "
294                               << "That is unrecoverable. The message was:" << endl
295                               << Exception::handleMessage() << endl;
296            abort();
297        }
298
299        // Calls the InputManager which sets up the input devices.
300        inputManager_ = new InputManager();
301
302        // Load the CEGUI interface
303        guiManager_ = new GUIManager(inputManager_->getMousePosition());
304
305        bGraphicsLoaded_ = true;
306        GameMode::bShowsGraphics_s = true;
307
308        // Load some sort of a debug overlay (only denoted by its name, "debug.oxo")
309        graphicsManager_->loadDebugOverlay();
310
311        // Create singletons associated with graphics (in other libraries)
312        orxout(internal_info) << "creating graphics scope:" << endl;
313        graphicsScope_ = new Scope<ScopeID::GRAPHICS>();
314
315        unloader.Dismiss();
316
317        orxout(internal_info) << "finished loading graphics in Core" << endl;
318    }
319
320    void Core::unloadGraphics()
321    {
322        orxout(internal_info) << "unloading graphics in Core" << endl;
323
324        safeObjectDelete(&graphicsScope_);
325        safeObjectDelete(&guiManager_);
326        safeObjectDelete(&inputManager_);
327        safeObjectDelete(&graphicsManager_);
328
329        // Load Ogre::Root again, but without the render system
330        try
331            { this->graphicsManager_ = new GraphicsManager(false); }
332        catch (...)
333        {
334            orxout(user_error) << "An exception occurred during 'unloadGraphics':" << Exception::handleMessage() << endl
335                               << "Another exception might be being handled which may lead to undefined behaviour!" << endl
336                               << "Terminating the program." << endl;
337            abort();
338        }
339
340        bGraphicsLoaded_ = false;
341        GameMode::bShowsGraphics_s = false;
342    }
343
344    /**
345    @note
346        The code of this function has been copied and adjusted from OGRE, an open source graphics engine.
347            (Object-oriented Graphics Rendering Engine)
348        For the latest info, see http://www.ogre3d.org/
349
350        Copyright (c) 2000-2008 Torus Knot Software Ltd
351
352        OGRE is licensed under the LGPL. For more info, see OGRE license.
353    */
354    void Core::setThreadAffinity(int limitToCPU)
355    {
356#ifdef ORXONOX_PLATFORM_WINDOWS
357
358        if (limitToCPU <= 0)
359            return;
360
361        unsigned int coreNr = limitToCPU - 1;
362        // Get the current process core mask
363        DWORD procMask;
364        DWORD sysMask;
365#  if _MSC_VER >= 1400 && defined (_M_X64)
366        GetProcessAffinityMask(GetCurrentProcess(), (PDWORD_PTR)&procMask, (PDWORD_PTR)&sysMask);
367#  else
368        GetProcessAffinityMask(GetCurrentProcess(), &procMask, &sysMask);
369#  endif
370
371        // If procMask is 0, consider there is only one core available
372        // (using 0 as procMask will cause an infinite loop below)
373        if (procMask == 0)
374            procMask = 1;
375
376        // if the core specified with coreNr is not available, take the lowest one
377        if (!(procMask & (1 << coreNr)))
378            coreNr = 0;
379
380        // Find the lowest core that this process uses and coreNr suggests
381        DWORD threadMask = 1;
382        while ((threadMask & procMask) == 0 || (threadMask < (1u << coreNr)))
383            threadMask <<= 1;
384
385        // Set affinity to the first core
386        SetThreadAffinityMask(GetCurrentThread(), threadMask);
387#endif
388    }
389
390    void Core::preUpdate(const Clock& time)
391    {
392        // Update UpdateListeners before general ticking
393        for (ObjectList<UpdateListener>::iterator it = ObjectList<UpdateListener>::begin(); it != ObjectList<UpdateListener>::end(); ++it)
394            it->preUpdate(time);
395        if (this->bGraphicsLoaded_)
396        {
397            // Process input events
398            this->inputManager_->preUpdate(time);
399            // Update GUI
400            this->guiManager_->preUpdate(time);
401        }
402        // Process console events and status line
403        if (this->ioConsole_ != NULL)
404            this->ioConsole_->preUpdate(time);
405        // Process thread commands
406        this->tclThreadManager_->preUpdate(time);
407    }
408
409    void Core::postUpdate(const Clock& time)
410    {
411        // Update UpdateListeners just before rendering
412        for (ObjectList<UpdateListener>::iterator it = ObjectList<UpdateListener>::begin(); it != ObjectList<UpdateListener>::end(); ++it)
413            it->postUpdate(time);
414        if (this->bGraphicsLoaded_)
415        {
416            // Render (doesn't throw)
417            this->graphicsManager_->postUpdate(time);
418        }
419    }
420}
Note: See TracBrowser for help on using the repository browser.