Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

define ScopeID as integer constants instead of an enum. this allows to extend it and add new scopes outside of core.

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