Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/libraries/core/Core.cc @ 9550

Last change on this file since 9550 was 9550, checked in by landauf, 11 years ago

merged testing branch back to trunk. unbelievable it took me 13 months to finish this chore…

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