Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/steering/src/libraries/core/Core.cc @ 6027

Last change on this file since 6027 was 5929, checked in by rgrieder, 15 years ago

Merged core5 branch back to the trunk.
Key features include clean level unloading and an extended XML event system.

Two important notes:
Delete your keybindings.ini files! * or you will still get parser errors when loading the key bindings.
Delete build_dir/lib/modules/libgamestates.module! * or orxonox won't start.
Best thing to do is to delete the build folder ;)

  • Property svn:eol-style set to native
File size: 16.5 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 <vector>
40
41#ifdef ORXONOX_PLATFORM_WINDOWS
42#  ifndef WIN32_LEAN_AND_MEAN
43#    define WIN32_LEAN_AND_MEAN
44#  endif
45#  include <windows.h>
46#  undef min
47#  undef max
48#endif
49
50#include "util/Clock.h"
51#include "util/Debug.h"
52#include "util/Exception.h"
53#include "util/SignalHandler.h"
54#include "PathConfig.h"
55#include "CommandExecutor.h"
56#include "CommandLine.h"
57#include "ConfigFileManager.h"
58#include "ConfigValueIncludes.h"
59#include "CoreIncludes.h"
60#include "DynLibManager.h"
61#include "GameMode.h"
62#include "GraphicsManager.h"
63#include "GUIManager.h"
64#include "Identifier.h"
65#include "Language.h"
66#include "LuaState.h"
67#include "ScopedSingletonManager.h"
68#include "Shell.h"
69#include "TclBind.h"
70#include "TclThreadManager.h"
71#include "input/InputManager.h"
72
73namespace orxonox
74{
75    //! Static pointer to the singleton
76    Core* Core::singletonPtr_s  = 0;
77
78    SetCommandLineArgument(settingsFile, "orxonox.ini").information("THE configuration file");
79#ifdef ORXONOX_PLATFORM_WINDOWS
80    SetCommandLineArgument(limitToCPU, 0).information("Limits the program to one cpu/core (1, 2, 3, etc.). 0 turns it off (default)");
81#endif
82
83    /**
84    @brief
85        Helper class for the Core singleton: we cannot derive
86        Core from OrxonoxClass because we need to handle the Identifier
87        destruction in the Core destructor.
88    */
89    class CoreConfiguration : public OrxonoxClass
90    {
91    public:
92        CoreConfiguration()
93        {
94        }
95
96        void initialise()
97        {
98            RegisterRootObject(CoreConfiguration);
99            this->setConfigValues();
100        }
101
102        /**
103            @brief Function to collect the SetConfigValue-macro calls.
104        */
105        void setConfigValues()
106        {
107#ifdef NDEBUG
108            const unsigned int defaultLevelConsole = 1;
109            const unsigned int defaultLevelLogfile = 3;
110            const unsigned int defaultLevelShell   = 1;
111#else
112            const unsigned int defaultLevelConsole = 3;
113            const unsigned int defaultLevelLogfile = 4;
114            const unsigned int defaultLevelShell   = 3;
115#endif
116            SetConfigValue(softDebugLevelConsole_, defaultLevelConsole)
117                .description("The maximal level of debug output shown in the console")
118                .callback(this, &CoreConfiguration::debugLevelChanged);
119            SetConfigValue(softDebugLevelLogfile_, defaultLevelLogfile)
120                .description("The maximal level of debug output shown in the logfile")
121                .callback(this, &CoreConfiguration::debugLevelChanged);
122            SetConfigValue(softDebugLevelShell_, defaultLevelShell)
123                .description("The maximal level of debug output shown in the ingame shell")
124                .callback(this, &CoreConfiguration::debugLevelChanged);
125
126            SetConfigValue(language_, Language::getInstance().defaultLanguage_)
127                .description("The language of the ingame text")
128                .callback(this, &CoreConfiguration::languageChanged);
129            SetConfigValue(bInitializeRandomNumberGenerator_, true)
130                .description("If true, all random actions are different each time you start the game")
131                .callback(this, &CoreConfiguration::initializeRandomNumberGenerator);
132        }
133
134        /**
135            @brief Callback function if the debug level has changed.
136        */
137        void debugLevelChanged()
138        {
139            // softDebugLevel_ is the maximum of the 3 variables
140            this->softDebugLevel_ = this->softDebugLevelConsole_;
141            if (this->softDebugLevelLogfile_ > this->softDebugLevel_)
142                this->softDebugLevel_ = this->softDebugLevelLogfile_;
143            if (this->softDebugLevelShell_ > this->softDebugLevel_)
144                this->softDebugLevel_ = this->softDebugLevelShell_;
145
146            OutputHandler::setSoftDebugLevel(OutputHandler::LD_All,     this->softDebugLevel_);
147            OutputHandler::setSoftDebugLevel(OutputHandler::LD_Console, this->softDebugLevelConsole_);
148            OutputHandler::setSoftDebugLevel(OutputHandler::LD_Logfile, this->softDebugLevelLogfile_);
149            OutputHandler::setSoftDebugLevel(OutputHandler::LD_Shell,   this->softDebugLevelShell_);
150        }
151
152        /**
153            @brief Callback function if the language has changed.
154        */
155        void languageChanged()
156        {
157            // Read the translation file after the language was configured
158            Language::getInstance().readTranslatedLanguageFile();
159        }
160
161        /**
162            @brief Sets the language in the config-file back to the default.
163        */
164        void resetLanguage()
165        {
166            ResetConfigValue(language_);
167        }
168
169        void initializeRandomNumberGenerator()
170        {
171            static bool bInitialized = false;
172            if (!bInitialized && this->bInitializeRandomNumberGenerator_)
173            {
174                srand(static_cast<unsigned int>(time(0)));
175                rand();
176                bInitialized = true;
177            }
178        }
179
180        int softDebugLevel_;                            //!< The debug level
181        int softDebugLevelConsole_;                     //!< The debug level for the console
182        int softDebugLevelLogfile_;                     //!< The debug level for the logfile
183        int softDebugLevelShell_;                       //!< The debug level for the ingame shell
184        std::string language_;                          //!< The language
185        bool bInitializeRandomNumberGenerator_;         //!< If true, srand(time(0)) is called
186    };
187
188
189    Core::Core(const std::string& cmdLine)
190        // Cleanup guard for identifier destruction (incl. XMLPort, configValues, consoleCommands)
191        : identifierDestroyer_(Identifier::destroyAllIdentifiers)
192        // Cleanup guard for external console commands that don't belong to an Identifier
193        , consoleCommandDestroyer_(CommandExecutor::destroyExternalCommands)
194        , configuration_(new CoreConfiguration()) // Don't yet create config values!
195        , bGraphicsLoaded_(false)
196    {
197        // Set the hard coded fixed paths
198        this->pathConfig_.reset(new PathConfig());
199
200        // Create a new dynamic library manager
201        this->dynLibManager_.reset(new DynLibManager());
202
203        // Load modules
204        const std::vector<std::string>& modulePaths = this->pathConfig_->getModulePaths();
205        for (std::vector<std::string>::const_iterator it = modulePaths.begin(); it != modulePaths.end(); ++it)
206        {
207            try
208            {
209                this->dynLibManager_->load(*it);
210            }
211            catch (...)
212            {
213                COUT(1) << "Couldn't load module \"" << *it << "\": " << Exception::handleMessage() << std::endl;
214            }
215        }
216
217        // Parse command line arguments AFTER the modules have been loaded (static code!)
218        CommandLine::parseCommandLine(cmdLine);
219
220        // Set configurable paths like log, config and media
221        this->pathConfig_->setConfigurablePaths();
222
223        // create a signal handler (only active for linux)
224        // This call is placed as soon as possible, but after the directories are set
225        this->signalHandler_.reset(new SignalHandler());
226        this->signalHandler_->doCatch(PathConfig::getExecutablePathString(), PathConfig::getLogPathString() + "orxonox_crash.log");
227
228        // Set the correct log path. Before this call, /tmp (Unix) or %TEMP% was used
229        OutputHandler::getOutStream().setLogPath(PathConfig::getLogPathString());
230
231        // Parse additional options file now that we know its path
232        CommandLine::parseFile();
233
234#ifdef ORXONOX_PLATFORM_WINDOWS
235        // limit the main thread to the first core so that QueryPerformanceCounter doesn't jump
236        // do this after ogre has initialised. Somehow Ogre changes the settings again (not through
237        // the timer though).
238        int limitToCPU = CommandLine::getValue("limitToCPU");
239        if (limitToCPU > 0)
240            setThreadAffinity(static_cast<unsigned int>(limitToCPU));
241#endif
242
243        // Manage ini files and set the default settings file (usually orxonox.ini)
244        this->configFileManager_.reset(new ConfigFileManager());
245        this->configFileManager_->setFilename(ConfigFileType::Settings,
246            CommandLine::getValue("settingsFile").getString());
247
248        // Required as well for the config values
249        this->languageInstance_.reset(new Language());
250
251        // creates the class hierarchy for all classes with factories
252        Identifier::createClassHierarchy();
253
254        // Do this soon after the ConfigFileManager has been created to open up the
255        // possibility to configure everything below here
256        this->configuration_->initialise();
257
258        // Load OGRE excluding the renderer and the render window
259        this->graphicsManager_.reset(new GraphicsManager(false));
260
261        // initialise Tcl
262        this->tclBind_.reset(new TclBind(PathConfig::getDataPathString()));
263        this->tclThreadManager_.reset(new TclThreadManager(tclBind_->getTclInterpreter()));
264
265        // create a shell
266        this->shell_.reset(new Shell());
267
268        // Create singletons that always exist (in other libraries)
269        this->rootScope_.reset(new Scope<ScopeID::Root>());
270    }
271
272    /**
273    @brief
274        All destruction code is handled by scoped_ptrs and ScopeGuards.
275    */
276    Core::~Core()
277    {
278    }
279
280    void Core::loadGraphics()
281    {
282        // Any exception should trigger this, even in upgradeToGraphics (see its remarks)
283        Loki::ScopeGuard unloader = Loki::MakeObjGuard(*this, &Core::unloadGraphics);
284
285        // Upgrade OGRE to receive a render window
286        graphicsManager_->upgradeToGraphics();
287
288        // Calls the InputManager which sets up the input devices.
289        inputManager_.reset(new InputManager());
290
291        // Load the CEGUI interface
292        guiManager_.reset(new GUIManager(graphicsManager_->getRenderWindow(),
293            inputManager_->getMousePosition(), graphicsManager_->isFullScreen()));
294
295        bGraphicsLoaded_ = true;
296        GameMode::bShowsGraphics_s = true;
297
298        // Load some sort of a debug overlay (only denoted by its name, "debug.oxo")
299        graphicsManager_->loadDebugOverlay();
300
301        // Create singletons associated with graphics (in other libraries)
302        graphicsScope_.reset(new Scope<ScopeID::Graphics>());
303
304        unloader.Dismiss();
305    }
306
307    void Core::unloadGraphics()
308    {
309        this->graphicsScope_.reset();
310        this->guiManager_.reset();
311        this->inputManager_.reset();
312        this->graphicsManager_.reset();
313
314        // Load Ogre::Root again, but without the render system
315        try
316            { this->graphicsManager_.reset(new GraphicsManager(false)); }
317        catch (...)
318        {
319            COUT(0) << "An exception occurred during 'unloadGraphics':" << Exception::handleMessage() << std::endl
320                    << "Another exception might be being handled which may lead to undefined behaviour!" << std::endl
321                    << "Terminating the program." << std::endl;
322            abort();
323        }
324
325        bGraphicsLoaded_ = false;
326        GameMode::bShowsGraphics_s = false;
327    }
328
329    /**
330        @brief Returns the softDebugLevel for the given device (returns a default-value if the class is right about to be created).
331        @param device The device
332        @return The softDebugLevel
333    */
334    /*static*/ int Core::getSoftDebugLevel(OutputHandler::OutputDevice device)
335    {
336        switch (device)
337        {
338        case OutputHandler::LD_All:
339            return Core::getInstance().configuration_->softDebugLevel_;
340        case OutputHandler::LD_Console:
341            return Core::getInstance().configuration_->softDebugLevelConsole_;
342        case OutputHandler::LD_Logfile:
343            return Core::getInstance().configuration_->softDebugLevelLogfile_;
344        case OutputHandler::LD_Shell:
345            return Core::getInstance().configuration_->softDebugLevelShell_;
346        default:
347            assert(0);
348            return 2;
349        }
350    }
351
352     /**
353        @brief Sets the softDebugLevel for the given device. Please use this only temporary and restore the value afterwards, as it overrides the configured value.
354        @param device The device
355        @param level The level
356    */
357    /*static*/ void Core::setSoftDebugLevel(OutputHandler::OutputDevice device, int level)
358    {
359        if (device == OutputHandler::LD_All)
360            Core::getInstance().configuration_->softDebugLevel_ = level;
361        else if (device == OutputHandler::LD_Console)
362            Core::getInstance().configuration_->softDebugLevelConsole_ = level;
363        else if (device == OutputHandler::LD_Logfile)
364            Core::getInstance().configuration_->softDebugLevelLogfile_ = level;
365        else if (device == OutputHandler::LD_Shell)
366            Core::getInstance().configuration_->softDebugLevelShell_ = level;
367
368        OutputHandler::setSoftDebugLevel(device, level);
369    }
370
371    /**
372        @brief Returns the configured language.
373    */
374    /*static*/ const std::string& Core::getLanguage()
375    {
376        return Core::getInstance().configuration_->language_;
377    }
378
379    /**
380        @brief Sets the language in the config-file back to the default.
381    */
382    /*static*/ void Core::resetLanguage()
383    {
384        Core::getInstance().configuration_->resetLanguage();
385    }
386
387    /**
388    @note
389        The code of this function has been copied and adjusted from OGRE, an open source graphics engine.
390            (Object-oriented Graphics Rendering Engine)
391        For the latest info, see http://www.ogre3d.org/
392
393        Copyright (c) 2000-2008 Torus Knot Software Ltd
394
395        OGRE is licensed under the LGPL. For more info, see OGRE license.
396    */
397    void Core::setThreadAffinity(int limitToCPU)
398    {
399#ifdef ORXONOX_PLATFORM_WINDOWS
400
401        if (limitToCPU <= 0)
402            return;
403
404        unsigned int coreNr = limitToCPU - 1;
405        // Get the current process core mask
406        DWORD procMask;
407        DWORD sysMask;
408#  if _MSC_VER >= 1400 && defined (_M_X64)
409        GetProcessAffinityMask(GetCurrentProcess(), (PDWORD_PTR)&procMask, (PDWORD_PTR)&sysMask);
410#  else
411        GetProcessAffinityMask(GetCurrentProcess(), &procMask, &sysMask);
412#  endif
413
414        // If procMask is 0, consider there is only one core available
415        // (using 0 as procMask will cause an infinite loop below)
416        if (procMask == 0)
417            procMask = 1;
418
419        // if the core specified with coreNr is not available, take the lowest one
420        if (!(procMask & (1 << coreNr)))
421            coreNr = 0;
422
423        // Find the lowest core that this process uses and coreNr suggests
424        DWORD threadMask = 1;
425        while ((threadMask & procMask) == 0 || (threadMask < (1u << coreNr)))
426            threadMask <<= 1;
427
428        // Set affinity to the first core
429        SetThreadAffinityMask(GetCurrentThread(), threadMask);
430#endif
431    }
432
433    void Core::preUpdate(const Clock& time)
434    {
435        // singletons from other libraries
436        ScopedSingletonManager::update<ScopeID::Root>(time);
437        if (this->bGraphicsLoaded_)
438        {
439            // process input events
440            this->inputManager_->update(time);
441            // process gui events
442            this->guiManager_->update(time);
443            // graphics singletons from other libraries
444            ScopedSingletonManager::update<ScopeID::Graphics>(time);
445        }
446        // process thread commands
447        this->tclThreadManager_->update(time);
448    }
449
450    void Core::postUpdate(const Clock& time)
451    {
452        if (this->bGraphicsLoaded_)
453        {
454            // Render (doesn't throw)
455            this->graphicsManager_->update(time);
456        }
457    }
458}
Note: See TracBrowser for help on using the repository browser.