Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 10373 was 10362, checked in by landauf, 10 years ago

use static identifier initializer to store the inheritance definition of abstract classes. this prevents that identifiers are used (via Class(Name)) before they are properly initialized.

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