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, 10 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.