Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

moved Scope and ScopedSingletonManager from util to core.
TODO I had to disable two tests in ScopeTest.cc because now that it runs in the core library, there are too many singletons which get loaded in graphics scope (with too many dependencies). this should be fixed

  • Property svn:eol-style set to native
File size: 21.1 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/ScopedSingletonManager.h"
60#include "util/SignalHandler.h"
61#include "PathConfig.h"
62#include "commandline/CommandLineIncludes.h"
63#include "config/ConfigFileManager.h"
64#include "config/ConfigValueIncludes.h"
65#include "CoreIncludes.h"
66#include "DynLibManager.h"
67#include "GameMode.h"
68#include "GraphicsManager.h"
69#include "GUIManager.h"
70#include "class/Identifier.h"
71#include "Language.h"
72#include "Loader.h"
73#include "LuaState.h"
74#include "command/ConsoleCommandManager.h"
75#include "command/IOConsole.h"
76#include "command/TclBind.h"
77#include "command/TclThreadManager.h"
78#include "input/InputManager.h"
79#include "object/ObjectList.h"
80#include "module/ModuleInstance.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    // register Core as an abstract class to avoid problems if the class hierarchy is created within Core-constructor
97    RegisterAbstractClass(Core).inheritsFrom<Configurable>();
98
99    Core::Core(const std::string& cmdLine)
100        : pathConfig_(NULL)
101        , dynLibManager_(NULL)
102        , signalHandler_(NULL)
103        , configFileManager_(NULL)
104        , languageInstance_(NULL)
105        , loaderInstance_(NULL)
106        , ioConsole_(NULL)
107        , tclBind_(NULL)
108        , tclThreadManager_(NULL)
109        , rootScope_(NULL)
110        , graphicsManager_(NULL)
111        , inputManager_(NULL)
112        , guiManager_(NULL)
113        , graphicsScope_(NULL)
114        , bGraphicsLoaded_(false)
115        , bStartIOConsole_(true)
116        , lastLevelTimestamp_(0)
117        , ogreConfigTimestamp_(0)
118        , bDevMode_(false)
119        , destructionHelper_(this)
120    {
121        orxout(internal_status) << "initializing Core object..." << endl;
122
123        // Set the hard coded fixed paths
124        this->pathConfig_ = new PathConfig();
125
126        // Create a new dynamic library manager
127        this->dynLibManager_ = new DynLibManager();
128
129        // Load modules
130        orxout(internal_info) << "Loading modules:" << endl;
131        const std::vector<std::string>& modulePaths = this->pathConfig_->getModulePaths();
132        for (std::vector<std::string>::const_iterator it = modulePaths.begin(); it != modulePaths.end(); ++it)
133        {
134            try
135            {
136                this->dynLibManager_->load(*it);
137            }
138            catch (...)
139            {
140                orxout(user_error) << "Couldn't load module \"" << *it << "\": " << Exception::handleMessage() << endl;
141            }
142        }
143
144        // TODO: initialize CommandLineParser here
145        // TODO: initialize ConsoleCommandManager here
146        ModuleInstance::getCurrentModuleInstance()->loadAllStaticallyInitializedInstances();
147
148        // Parse command line arguments AFTER the modules have been loaded (static code!)
149        CommandLineParser::parse(cmdLine);
150
151        // Set configurable paths like log, config and media
152        this->pathConfig_->setConfigurablePaths();
153
154        orxout(internal_info) << "Root path:       " << PathConfig::getRootPathString() << endl;
155        orxout(internal_info) << "Executable path: " << PathConfig::getExecutablePathString() << endl;
156        orxout(internal_info) << "Data path:       " << PathConfig::getDataPathString() << endl;
157        orxout(internal_info) << "Ext. data path:  " << PathConfig::getExternalDataPathString() << endl;
158        orxout(internal_info) << "Config path:     " << PathConfig::getConfigPathString() << endl;
159        orxout(internal_info) << "Log path:        " << PathConfig::getLogPathString() << endl;
160        orxout(internal_info) << "Modules path:    " << PathConfig::getModulePathString() << endl;
161
162        // create a signal handler (only active for Linux)
163        // This call is placed as soon as possible, but after the directories are set
164        this->signalHandler_ = new SignalHandler();
165        this->signalHandler_->doCatch(PathConfig::getExecutablePathString(), PathConfig::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        RegisterObject(Core);
189        orxout(internal_info) << "configuring Core" << endl;
190        this->setConfigValues();
191
192        // Set the correct log path and rewrite the log file with the correct log levels
193        OutputManager::getInstance().getLogWriter()->setLogDirectory(PathConfig::getLogPathString());
194
195#if !defined(ORXONOX_PLATFORM_APPLE) && !defined(ORXONOX_USE_WINMAIN)
196        // Create persistent IO console
197        if (CommandLineParser::getValue("noIOConsole").get<bool>())
198        {
199            ModifyConfigValue(bStartIOConsole_, tset, false);
200        }
201        if (this->bStartIOConsole_)
202        {
203            orxout(internal_info) << "creating IO console" << endl;
204            this->ioConsole_ = new IOConsole();
205        }
206#endif
207
208        // creates the class hierarchy for all classes with factories
209        orxout(internal_info) << "creating class hierarchy" << endl;
210        IdentifierManager::getInstance().createClassHierarchy();
211
212        // Loader
213        this->loaderInstance_ = new Loader();
214
215        // Load OGRE excluding the renderer and the render window
216        orxout(internal_info) << "creating GraphicsManager:" << endl;
217        this->graphicsManager_ = new GraphicsManager(false);
218
219        // initialise Tcl
220        this->tclBind_ = new TclBind(PathConfig::getDataPathString());
221        this->tclThreadManager_ = new TclThreadManager(tclBind_->getTclInterpreter());
222
223        // Create singletons that always exist (in other libraries)
224        orxout(internal_info) << "creating root scope:" << endl;
225        this->rootScope_ = new Scope<ScopeID::Root>();
226
227        // Generate documentation instead of normal run?
228        std::string docFilename;
229        CommandLineParser::getValue("generateDoc", &docFilename);
230        if (!docFilename.empty())
231        {
232            std::ofstream docFile(docFilename.c_str());
233            if (docFile.is_open())
234            {
235                CommandLineParser::generateDoc(docFile);
236                docFile.close();
237            }
238            else
239                orxout(internal_error) << "Could not open file for documentation writing" << endl;
240        }
241
242        orxout(internal_status) << "finished initializing Core object" << endl;
243    }
244
245    void Core::destroy()
246    {
247        orxout(internal_status) << "destroying Core object..." << endl;
248
249        // Remove us from the object lists again to avoid problems when destroying them
250        this->unregisterObject();
251
252        safeObjectDelete(&graphicsScope_);
253        safeObjectDelete(&guiManager_);
254        safeObjectDelete(&inputManager_);
255        safeObjectDelete(&graphicsManager_);
256        safeObjectDelete(&rootScope_);
257        safeObjectDelete(&tclThreadManager_);
258        safeObjectDelete(&tclBind_);
259        safeObjectDelete(&ioConsole_);
260        safeObjectDelete(&loaderInstance_);
261        safeObjectDelete(&languageInstance_);
262        safeObjectDelete(&configFileManager_);
263        ConsoleCommandManager::destroyAll();
264        Context::setRootContext(NULL);
265        IdentifierManager::getInstance().destroyAllIdentifiers();
266        safeObjectDelete(&signalHandler_);
267        safeObjectDelete(&dynLibManager_);
268        safeObjectDelete(&pathConfig_);
269
270        orxout(internal_status) << "finished destroying Core object" << endl;
271    }
272
273    //! Function to collect the SetConfigValue-macro calls.
274    void Core::setConfigValues()
275    {
276        SetConfigValueExternal(OutputManager::getInstance().getLogWriter()->configurableMaxLevel_,
277                               OutputManager::getInstance().getLogWriter()->getConfigurableSectionName(),
278                               OutputManager::getInstance().getLogWriter()->getConfigurableMaxLevelName(),
279                               OutputManager::getInstance().getLogWriter()->configurableMaxLevel_)
280            .description("The maximum level of output shown in the log file")
281            .callback(static_cast<BaseWriter*>(OutputManager::getInstance().getLogWriter()), &BaseWriter::changedConfigurableLevel);
282        SetConfigValueExternal(OutputManager::getInstance().getLogWriter()->configurableAdditionalContextsMaxLevel_,
283                               OutputManager::getInstance().getLogWriter()->getConfigurableSectionName(),
284                               OutputManager::getInstance().getLogWriter()->getConfigurableAdditionalContextsMaxLevelName(),
285                               OutputManager::getInstance().getLogWriter()->configurableAdditionalContextsMaxLevel_)
286            .description("The maximum level of output shown in the log file for additional contexts")
287            .callback(static_cast<BaseWriter*>(OutputManager::getInstance().getLogWriter()), &BaseWriter::changedConfigurableAdditionalContextsLevel);
288        SetConfigValueExternal(OutputManager::getInstance().getLogWriter()->configurableAdditionalContexts_,
289                               OutputManager::getInstance().getLogWriter()->getConfigurableSectionName(),
290                               OutputManager::getInstance().getLogWriter()->getConfigurableAdditionalContextsName(),
291                               OutputManager::getInstance().getLogWriter()->configurableAdditionalContexts_)
292            .description("Additional output contexts shown in the log file")
293            .callback(static_cast<BaseWriter*>(OutputManager::getInstance().getLogWriter()), &BaseWriter::changedConfigurableAdditionalContexts);
294
295        SetConfigValue(bDevMode_, PathConfig::buildDirectoryRun())
296            .description("Developer mode. If not set, hides some things from the user to not confuse him.")
297            .callback(this, &Core::devModeChanged);
298        SetConfigValue(language_, Language::getInstance().defaultLanguage_)
299            .description("The language of the in game text")
300            .callback(this, &Core::languageChanged);
301        SetConfigValue(bInitRandomNumberGenerator_, true)
302            .description("If true, all random actions are different each time you start the game")
303            .callback(this, &Core::initRandomNumberGenerator);
304        SetConfigValue(bStartIOConsole_, true)
305            .description("Set to false if you don't want to use the IOConsole (for Lua debugging for instance)");
306        SetConfigValue(lastLevelTimestamp_, 0)
307            .description("Timestamp when the last level was started.");
308        SetConfigValue(ogreConfigTimestamp_, 0)
309            .description("Timestamp when the ogre config file was changed.");
310    }
311
312    /** Callback function for changes in the dev mode that affect debug levels.
313        The function behaves according to these rules:
314        - 'normal' mode is defined based on where the program was launched: if
315          the launch path was the build directory, development mode \c on is
316          normal, otherwise normal means development mode \c off.
317        - Debug levels should not be hard configured (\c config instead of
318          \c tconfig) in non 'normal' mode to avoid strange behaviour.
319        - Changing the development mode from 'normal' to the other state will
320          immediately change the debug levels to predefined values which can be
321          reconfigured with \c tconfig.
322    @note
323        The debug levels for the IOConsole and the InGameConsole can be found
324        in the Shell class. The same rules apply.
325    */
326    void Core::devModeChanged()
327    {
328        // Inform listeners
329        ObjectList<DevModeListener>::iterator it = ObjectList<DevModeListener>::begin();
330        for (; it != ObjectList<DevModeListener>::end(); ++it)
331            it->devModeChanged(bDevMode_);
332    }
333
334    //! Callback function if the language has changed.
335    void Core::languageChanged()
336    {
337        // Read the translation file after the language was configured
338        Language::getInstance().readTranslatedLanguageFile();
339    }
340
341    void Core::initRandomNumberGenerator()
342    {
343        static bool bInitialized = false;
344        if (!bInitialized && this->bInitRandomNumberGenerator_)
345        {
346            srand(static_cast<unsigned int>(time(0)));
347            rand();
348            bInitialized = true;
349        }
350    }
351
352    void Core::loadGraphics()
353    {
354        orxout(internal_info) << "loading graphics in Core" << endl;
355
356        // Any exception should trigger this, even in upgradeToGraphics (see its remarks)
357        Loki::ScopeGuard unloader = Loki::MakeObjGuard(*this, &Core::unloadGraphics);
358
359        // Upgrade OGRE to receive a render window
360        try
361        {
362            graphicsManager_->upgradeToGraphics();
363        }
364        catch (const InitialisationFailedException&)
365        {
366            // Exit the application if the Ogre config dialog was canceled
367            orxout(user_error) << Exception::handleMessage() << endl;
368            exit(EXIT_FAILURE);
369        }
370        catch (...)
371        {
372            // Recovery from this is very difficult. It requires to completely
373            // destroy Ogre related objects and load again (without graphics).
374            // However since Ogre 1.7 there seems to be a problem when Ogre
375            // throws an exception and the graphics engine then gets destroyed
376            // and reloaded between throw and catch (access violation in MSVC).
377            // That's why we abort completely and only display the exception.
378            orxout(user_error) << "An exception occurred during upgrade to graphics. "
379                               << "That is unrecoverable. The message was:" << endl
380                               << Exception::handleMessage() << endl;
381            abort();
382        }
383
384        // Calls the InputManager which sets up the input devices.
385        inputManager_ = new InputManager();
386
387        // Load the CEGUI interface
388        guiManager_ = new GUIManager(inputManager_->getMousePosition());
389
390        bGraphicsLoaded_ = true;
391        GameMode::bShowsGraphics_s = true;
392
393        // Load some sort of a debug overlay (only denoted by its name, "debug.oxo")
394        graphicsManager_->loadDebugOverlay();
395
396        // Create singletons associated with graphics (in other libraries)
397        orxout(internal_info) << "creating graphics scope:" << endl;
398        graphicsScope_ = new Scope<ScopeID::Graphics>();
399
400        unloader.Dismiss();
401
402        orxout(internal_info) << "finished loading graphics in Core" << endl;
403    }
404
405    void Core::unloadGraphics()
406    {
407        orxout(internal_info) << "unloading graphics in Core" << endl;
408
409        safeObjectDelete(&graphicsScope_);
410        safeObjectDelete(&guiManager_);
411        safeObjectDelete(&inputManager_);
412        safeObjectDelete(&graphicsManager_);
413
414        // Load Ogre::Root again, but without the render system
415        try
416            { this->graphicsManager_ = new GraphicsManager(false); }
417        catch (...)
418        {
419            orxout(user_error) << "An exception occurred during 'unloadGraphics':" << Exception::handleMessage() << endl
420                               << "Another exception might be being handled which may lead to undefined behaviour!" << endl
421                               << "Terminating the program." << endl;
422            abort();
423        }
424
425        bGraphicsLoaded_ = false;
426        GameMode::bShowsGraphics_s = false;
427    }
428
429    //! Sets the language in the config-file back to the default.
430    void Core::resetLanguage()
431    {
432        ResetConfigValue(language_);
433    }
434
435    /**
436    @note
437        The code of this function has been copied and adjusted from OGRE, an open source graphics engine.
438            (Object-oriented Graphics Rendering Engine)
439        For the latest info, see http://www.ogre3d.org/
440
441        Copyright (c) 2000-2008 Torus Knot Software Ltd
442
443        OGRE is licensed under the LGPL. For more info, see OGRE license.
444    */
445    void Core::setThreadAffinity(int limitToCPU)
446    {
447#ifdef ORXONOX_PLATFORM_WINDOWS
448
449        if (limitToCPU <= 0)
450            return;
451
452        unsigned int coreNr = limitToCPU - 1;
453        // Get the current process core mask
454        DWORD procMask;
455        DWORD sysMask;
456#  if _MSC_VER >= 1400 && defined (_M_X64)
457        GetProcessAffinityMask(GetCurrentProcess(), (PDWORD_PTR)&procMask, (PDWORD_PTR)&sysMask);
458#  else
459        GetProcessAffinityMask(GetCurrentProcess(), &procMask, &sysMask);
460#  endif
461
462        // If procMask is 0, consider there is only one core available
463        // (using 0 as procMask will cause an infinite loop below)
464        if (procMask == 0)
465            procMask = 1;
466
467        // if the core specified with coreNr is not available, take the lowest one
468        if (!(procMask & (1 << coreNr)))
469            coreNr = 0;
470
471        // Find the lowest core that this process uses and coreNr suggests
472        DWORD threadMask = 1;
473        while ((threadMask & procMask) == 0 || (threadMask < (1u << coreNr)))
474            threadMask <<= 1;
475
476        // Set affinity to the first core
477        SetThreadAffinityMask(GetCurrentThread(), threadMask);
478#endif
479    }
480
481    void Core::preUpdate(const Clock& time)
482    {
483        // Update singletons before general ticking
484        ScopedSingletonManager::preUpdate<ScopeID::Root>(time);
485        if (this->bGraphicsLoaded_)
486        {
487            // Process input events
488            this->inputManager_->preUpdate(time);
489            // Update GUI
490            this->guiManager_->preUpdate(time);
491            // Update singletons before general ticking
492            ScopedSingletonManager::preUpdate<ScopeID::Graphics>(time);
493        }
494        // Process console events and status line
495        if (this->ioConsole_ != NULL)
496            this->ioConsole_->preUpdate(time);
497        // Process thread commands
498        this->tclThreadManager_->preUpdate(time);
499    }
500
501    void Core::postUpdate(const Clock& time)
502    {
503        // Update singletons just before rendering
504        ScopedSingletonManager::postUpdate<ScopeID::Root>(time);
505        if (this->bGraphicsLoaded_)
506        {
507            // Update singletons just before rendering
508            ScopedSingletonManager::postUpdate<ScopeID::Graphics>(time);
509            // Render (doesn't throw)
510            this->graphicsManager_->postUpdate(time);
511        }
512    }
513
514    void Core::updateLastLevelTimestamp()
515    {
516        ModifyConfigValue(lastLevelTimestamp_, set, static_cast<long long>(time(NULL)));
517    }
518
519    void Core::updateOgreConfigTimestamp()
520    {
521        ModifyConfigValue(ogreConfigTimestamp_, set, static_cast<long long>(time(NULL)));
522    }
523
524
525    RegisterAbstractClass(DevModeListener).inheritsFrom<Listable>();
526
527    DevModeListener::DevModeListener()
528    {
529        RegisterObject(DevModeListener);
530    }
531}
Note: See TracBrowser for help on using the repository browser.