Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 10362 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
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 "util/Scope.h"
59#include "util/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 "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
81namespace orxonox
82{
83    //! Static pointer to the singleton
84    Core* Core::singletonPtr_s  = 0;
85
86    SetCommandLineArgument(settingsFile, "orxonox.ini").information("THE configuration file");
87#if !defined(ORXONOX_PLATFORM_APPLE) && !defined(ORXONOX_USE_WINMAIN)
88    SetCommandLineSwitch(noIOConsole).information("Use this if you don't want to use the IOConsole (for instance for Lua debugging)");
89#endif
90
91#ifdef ORXONOX_PLATFORM_WINDOWS
92    SetCommandLineArgument(limitToCPU, 0).information("Limits the program to one CPU/core (1, 2, 3, etc.). Default is off = 0.");
93#endif
94
95    // register Core as an abstract class to avoid problems if the class hierarchy is created within Core-constructor
96    RegisterAbstractClass(Core).inheritsFrom<Configurable>();
97
98    Core::Core(const std::string& cmdLine)
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)
112        , bGraphicsLoaded_(false)
113        , bStartIOConsole_(true)
114        , lastLevelTimestamp_(0)
115        , ogreConfigTimestamp_(0)
116        , bDevMode_(false)
117        , destructionHelper_(this)
118    {
119        orxout(internal_status) << "initializing Core object..." << endl;
120
121        // Set the hard coded fixed paths
122        this->pathConfig_ = new PathConfig();
123
124        // Create a new dynamic library manager
125        this->dynLibManager_ = new DynLibManager();
126
127        // Load modules
128        orxout(internal_info) << "Loading modules:" << endl;
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)
131        {
132            try
133            {
134                this->dynLibManager_->load(*it);
135            }
136            catch (...)
137            {
138                orxout(user_error) << "Couldn't load module \"" << *it << "\": " << Exception::handleMessage() << endl;
139            }
140        }
141
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->pathConfig_->setConfigurablePaths();
151
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
160        // create a signal handler (only active for Linux)
161        // This call is placed as soon as possible, but after the directories are set
162        this->signalHandler_ = new SignalHandler();
163        this->signalHandler_->doCatch(PathConfig::getExecutablePathString(), PathConfig::getLogPathString() + "orxonox_crash.log");
164
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).
169        int limitToCPU = CommandLineParser::getValue("limitToCPU");
170        if (limitToCPU > 0)
171            setThreadAffinity(static_cast<unsigned int>(limitToCPU));
172#endif
173
174        // Manage ini files and set the default settings file (usually orxonox.ini)
175        orxout(internal_info) << "Loading config:" << endl;
176        this->configFileManager_ = new ConfigFileManager();
177        this->configFileManager_->setFilename(ConfigFileType::Settings,
178            CommandLineParser::getValue("settingsFile").get<std::string>());
179
180        // Required as well for the config values
181        orxout(internal_info) << "Loading language:" << endl;
182        this->languageInstance_ = new Language();
183
184        // Do this soon after the ConfigFileManager has been created to open up the
185        // possibility to configure everything below here
186        RegisterObject(Core);
187        orxout(internal_info) << "configuring Core" << endl;
188        this->setConfigValues();
189
190        // Set the correct log path and rewrite the log file with the correct log levels
191        OutputManager::getInstance().getLogWriter()->setLogDirectory(PathConfig::getLogPathString());
192
193#if !defined(ORXONOX_PLATFORM_APPLE) && !defined(ORXONOX_USE_WINMAIN)
194        // Create persistent IO console
195        if (CommandLineParser::getValue("noIOConsole").get<bool>())
196        {
197            ModifyConfigValue(bStartIOConsole_, tset, false);
198        }
199        if (this->bStartIOConsole_)
200        {
201            orxout(internal_info) << "creating IO console" << endl;
202            this->ioConsole_ = new IOConsole();
203        }
204#endif
205
206        // creates the class hierarchy for all classes with factories
207        orxout(internal_info) << "creating class hierarchy" << endl;
208        IdentifierManager::getInstance().createClassHierarchy();
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(PathConfig::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        // Remove us from the object lists again to avoid problems when destroying them
245        this->unregisterObject();
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_);
257        ConsoleCommandManager::destroyAll();
258        Context::setRootContext(NULL);
259        IdentifierManager::getInstance().destroyAllIdentifiers();
260        safeObjectDelete(&signalHandler_);
261        safeObjectDelete(&dynLibManager_);
262        safeObjectDelete(&pathConfig_);
263
264        orxout(internal_status) << "finished destroying Core object" << endl;
265    }
266
267    //! Function to collect the SetConfigValue-macro calls.
268    void Core::setConfigValues()
269    {
270        SetConfigValueExternal(OutputManager::getInstance().getLogWriter()->configurableMaxLevel_,
271                               OutputManager::getInstance().getLogWriter()->getConfigurableSectionName(),
272                               OutputManager::getInstance().getLogWriter()->getConfigurableMaxLevelName(),
273                               OutputManager::getInstance().getLogWriter()->configurableMaxLevel_)
274            .description("The maximum level of output shown in the log file")
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_)
280            .description("The maximum level of output shown in the log file for additional contexts")
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_)
286            .description("Additional output contexts shown in the log file")
287            .callback(static_cast<BaseWriter*>(OutputManager::getInstance().getLogWriter()), &BaseWriter::changedConfigurableAdditionalContexts);
288
289        SetConfigValue(bDevMode_, PathConfig::buildDirectoryRun())
290            .description("Developer mode. If not set, hides some things from the user to not confuse him.")
291            .callback(this, &Core::devModeChanged);
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);
298        SetConfigValue(bStartIOConsole_, true)
299            .description("Set to false if you don't want to use the IOConsole (for Lua debugging for instance)");
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.");
304    }
305
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
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
346    void Core::loadGraphics()
347    {
348        orxout(internal_info) << "loading graphics in Core" << endl;
349
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
354        try
355        {
356            graphicsManager_->upgradeToGraphics();
357        }
358        catch (const InitialisationFailedException&)
359        {
360            // Exit the application if the Ogre config dialog was canceled
361            orxout(user_error) << Exception::handleMessage() << endl;
362            exit(EXIT_FAILURE);
363        }
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.
372            orxout(user_error) << "An exception occurred during upgrade to graphics. "
373                               << "That is unrecoverable. The message was:" << endl
374                               << Exception::handleMessage() << endl;
375            abort();
376        }
377
378        // Calls the InputManager which sets up the input devices.
379        inputManager_ = new InputManager();
380
381        // Load the CEGUI interface
382        guiManager_ = new GUIManager(inputManager_->getMousePosition());
383
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)
391        orxout(internal_info) << "creating graphics scope:" << endl;
392        graphicsScope_ = new Scope<ScopeID::Graphics>();
393
394        unloader.Dismiss();
395
396        orxout(internal_info) << "finished loading graphics in Core" << endl;
397    }
398
399    void Core::unloadGraphics()
400    {
401        orxout(internal_info) << "unloading graphics in Core" << endl;
402
403        safeObjectDelete(&graphicsScope_);
404        safeObjectDelete(&guiManager_);
405        safeObjectDelete(&inputManager_);
406        safeObjectDelete(&graphicsManager_);
407
408        // Load Ogre::Root again, but without the render system
409        try
410            { this->graphicsManager_ = new GraphicsManager(false); }
411        catch (...)
412        {
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;
416            abort();
417        }
418
419        bGraphicsLoaded_ = false;
420        GameMode::bShowsGraphics_s = false;
421    }
422
423    //! Sets the language in the config-file back to the default.
424    void Core::resetLanguage()
425    {
426        ResetConfigValue(language_);
427    }
428
429    /**
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.
438    */
439    void Core::setThreadAffinity(int limitToCPU)
440    {
441#ifdef ORXONOX_PLATFORM_WINDOWS
442
443        if (limitToCPU <= 0)
444            return;
445
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
455
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
473    }
474
475    void Core::preUpdate(const Clock& time)
476    {
477        // Update singletons before general ticking
478        ScopedSingletonManager::preUpdate<ScopeID::Root>(time);
479        if (this->bGraphicsLoaded_)
480        {
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);
487        }
488        // Process console events and status line
489        if (this->ioConsole_ != NULL)
490            this->ioConsole_->preUpdate(time);
491        // Process thread commands
492        this->tclThreadManager_->preUpdate(time);
493    }
494
495    void Core::postUpdate(const Clock& time)
496    {
497        // Update singletons just before rendering
498        ScopedSingletonManager::postUpdate<ScopeID::Root>(time);
499        if (this->bGraphicsLoaded_)
500        {
501            // Update singletons just before rendering
502            ScopedSingletonManager::postUpdate<ScopeID::Graphics>(time);
503            // Render (doesn't throw)
504            this->graphicsManager_->postUpdate(time);
505        }
506    }
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    }
517
518
519    RegisterAbstractClass(DevModeListener).inheritsFrom<Listable>();
520
521    DevModeListener::DevModeListener()
522    {
523        RegisterObject(DevModeListener);
524    }
525}
Note: See TracBrowser for help on using the repository browser.