Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/output/src/libraries/core/Core.cc @ 8830

Last change on this file since 8830 was 8830, checked in by landauf, 13 years ago

added some output (user and internal) throughout the initialization of the game, graphics, and game states

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