Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

A context is now defined by a struct instead of only a mask.

Introduced sub-contexts. Sub-contexts of the same main-context share the same mask, but have a different ID.
Main-contexts are filtered using a bitmask which happens for every line of output and is very fast.
Sub-contexts are filtered using a set which is slow but happens only if a specific sub-context is enabled in the config file which is usually not the case.

The concept of filtering normal output + additional contexts was moved from BaseWriter directly to OutputListener and OutputManager which makes the whole system faster.
BaseWriter now calls registerContext() for each configured output context, which basically allows the usage of more than 64 contexts as long as these contexts are not used before loading the config file. Though by design it's not recommended.

  • Property svn:eol-style set to native
File size: 19.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/Scope.h"
58#include "util/ScopedSingletonManager.h"
59#include "util/SignalHandler.h"
60#include "PathConfig.h"
61#include "CommandLineParser.h"
62#include "ConfigFileManager.h"
63#include "ConfigValueIncludes.h"
64#include "CoreIncludes.h"
65#include "DynLibManager.h"
66#include "GameMode.h"
67#include "GraphicsManager.h"
68#include "GUIManager.h"
69#include "Identifier.h"
70#include "Language.h"
71#include "LuaState.h"
72#include "ObjectList.h"
73#include "command/ConsoleCommand.h"
74#include "command/IOConsole.h"
75#include "command/TclBind.h"
76#include "command/TclThreadManager.h"
77#include "input/InputManager.h"
78
79namespace orxonox
80{
81    //! Static pointer to the singleton
82    Core* Core::singletonPtr_s  = 0;
83
84    SetCommandLineArgument(settingsFile, "orxonox.ini").information("THE configuration file");
85#if !defined(ORXONOX_PLATFORM_APPLE) && !defined(ORXONOX_USE_WINMAIN)
86    SetCommandLineSwitch(noIOConsole).information("Use this if you don't want to use the IOConsole (for instance for Lua debugging)");
87#endif
88
89#ifdef ORXONOX_PLATFORM_WINDOWS
90    SetCommandLineArgument(limitToCPU, 0).information("Limits the program to one CPU/core (1, 2, 3, etc.). Default is off = 0.");
91#endif
92
93    Core::Core(const std::string& cmdLine)
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)
107        , bGraphicsLoaded_(false)
108        , bStartIOConsole_(true)
109        , lastLevelTimestamp_(0)
110        , ogreConfigTimestamp_(0)
111        , bDevMode_(false)
112        , destructionHelper_(this)
113    {
114        orxout(internal_status) << "initializing Core object..." << endl;
115
116        // Set the hard coded fixed paths
117        this->pathConfig_ = new PathConfig();
118
119        // Create a new dynamic library manager
120        this->dynLibManager_ = new DynLibManager();
121
122        // Load modules
123        orxout(internal_info) << "Loading modules:" << endl;
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)
126        {
127            try
128            {
129                this->dynLibManager_->load(*it);
130            }
131            catch (...)
132            {
133                orxout(user_error) << "Couldn't load module \"" << *it << "\": " << Exception::handleMessage() << endl;
134            }
135        }
136
137        // Parse command line arguments AFTER the modules have been loaded (static code!)
138        CommandLineParser::parse(cmdLine);
139
140        // Set configurable paths like log, config and media
141        this->pathConfig_->setConfigurablePaths();
142
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
151        // create a signal handler (only active for Linux)
152        // This call is placed as soon as possible, but after the directories are set
153        this->signalHandler_ = new SignalHandler();
154        this->signalHandler_->doCatch(PathConfig::getExecutablePathString(), PathConfig::getLogPathString() + "orxonox_crash.log");
155
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).
160        int limitToCPU = CommandLineParser::getValue("limitToCPU");
161        if (limitToCPU > 0)
162            setThreadAffinity(static_cast<unsigned int>(limitToCPU));
163#endif
164
165        // Manage ini files and set the default settings file (usually orxonox.ini)
166        orxout(internal_info) << "Loading config:" << endl;
167        this->configFileManager_ = new ConfigFileManager();
168        this->configFileManager_->setFilename(ConfigFileType::Settings,
169            CommandLineParser::getValue("settingsFile").getString());
170
171        // Required as well for the config values
172        orxout(internal_info) << "Loading language:" << endl;
173        this->languageInstance_ = new Language();
174
175        // Do this soon after the ConfigFileManager has been created to open up the
176        // possibility to configure everything below here
177        RegisterRootObject(Core);
178        orxout(internal_info) << "configuring Core" << endl;
179        this->setConfigValues();
180
181        // Set the correct log path and rewrite the log file with the correct log levels
182        LogWriter::getInstance().setLogPath(PathConfig::getLogPathString());
183
184#if !defined(ORXONOX_PLATFORM_APPLE) && !defined(ORXONOX_USE_WINMAIN)
185        // Create persistent IO console
186        if (CommandLineParser::getValue("noIOConsole").getBool())
187        {
188            ModifyConfigValue(bStartIOConsole_, tset, false);
189        }
190        if (this->bStartIOConsole_)
191        {
192            orxout(internal_info) << "creating IO console" << endl;
193            this->ioConsole_ = new IOConsole();
194        }
195#endif
196
197        // creates the class hierarchy for all classes with factories
198        orxout(internal_info) << "creating class hierarchy" << endl;
199        Identifier::createClassHierarchy();
200
201        // Load OGRE excluding the renderer and the render window
202        orxout(internal_info) << "creating GraphicsManager:" << endl;
203        this->graphicsManager_ = new GraphicsManager(false);
204
205        // initialise Tcl
206        this->tclBind_ = new TclBind(PathConfig::getDataPathString());
207        this->tclThreadManager_ = new TclThreadManager(tclBind_->getTclInterpreter());
208
209        // Create singletons that always exist (in other libraries)
210        orxout(internal_info) << "creating root scope:" << endl;
211        this->rootScope_ = new Scope<ScopeID::Root>();
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
225                orxout(internal_error) << "Could not open file for documentation writing" << endl;
226        }
227
228        orxout(internal_status) << "finished initializing Core object" << endl;
229    }
230
231    void Core::destroy()
232    {
233        orxout(internal_status) << "destroying Core object..." << endl;
234
235        // Remove us from the object lists again to avoid problems when destroying them
236        this->unregisterObject();
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_);
253
254        orxout(internal_status) << "finished destroying Core object" << endl;
255    }
256
257    //! Function to collect the SetConfigValue-macro calls.
258    void Core::setConfigValues()
259    {
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::changedConfigurableLevel);
266        SetConfigValueExternal(LogWriter::getInstance().configurableAdditionalContextsMaxLevel_,
267                               LogWriter::getInstance().getConfigurableSectionName(),
268                               LogWriter::getInstance().getConfigurableAdditionalContextsMaxLevelName(),
269                               LogWriter::getInstance().configurableAdditionalContextsMaxLevel_)
270            .description("The maximum level of output shown in the log file for additional contexts")
271            .callback(static_cast<BaseWriter*>(&LogWriter::getInstance()), &BaseWriter::changedConfigurableAdditionalContextsLevel);
272        SetConfigValueExternal(LogWriter::getInstance().configurableAdditionalContexts_,
273                               LogWriter::getInstance().getConfigurableSectionName(),
274                               LogWriter::getInstance().getConfigurableAdditionalContextsName(),
275                               LogWriter::getInstance().configurableAdditionalContexts_)
276            .description("Additional output contexts shown in the log file")
277            .callback(static_cast<BaseWriter*>(&LogWriter::getInstance()), &BaseWriter::changedConfigurableAdditionalContexts);
278
279        SetConfigValue(bDevMode_, PathConfig::buildDirectoryRun())
280            .description("Developer mode. If not set, hides some things from the user to not confuse him.")
281            .callback(this, &Core::devModeChanged);
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);
288        SetConfigValue(bStartIOConsole_, true)
289            .description("Set to false if you don't want to use the IOConsole (for Lua debugging for instance)");
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.");
294    }
295
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
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
336    void Core::loadGraphics()
337    {
338        orxout(internal_info) << "loading graphics in Core" << endl;
339       
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
344        try
345        {
346            graphicsManager_->upgradeToGraphics();
347        }
348        catch (const InitialisationFailedException&)
349        {
350            // Exit the application if the Ogre config dialog was canceled
351            orxout(user_error) << Exception::handleMessage() << endl;
352            exit(EXIT_FAILURE);
353        }
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.
362            orxout(user_error) << "An exception occurred during upgrade to graphics. "
363                               << "That is unrecoverable. The message was:" << endl
364                               << Exception::handleMessage() << endl;
365            abort();
366        }
367
368        // Calls the InputManager which sets up the input devices.
369        inputManager_ = new InputManager();
370
371        // Load the CEGUI interface
372        guiManager_ = new GUIManager(inputManager_->getMousePosition());
373
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)
381        orxout(internal_info) << "creating graphics scope:" << endl;
382        graphicsScope_ = new Scope<ScopeID::Graphics>();
383
384        unloader.Dismiss();
385
386        orxout(internal_info) << "finished loading graphics in Core" << endl;
387    }
388
389    void Core::unloadGraphics()
390    {
391        orxout(internal_info) << "unloading graphics in Core" << endl;
392
393        safeObjectDelete(&graphicsScope_);
394        safeObjectDelete(&guiManager_);
395        safeObjectDelete(&inputManager_);
396        safeObjectDelete(&graphicsManager_);
397
398        // Load Ogre::Root again, but without the render system
399        try
400            { this->graphicsManager_ = new GraphicsManager(false); }
401        catch (...)
402        {
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;
406            abort();
407        }
408
409        bGraphicsLoaded_ = false;
410        GameMode::bShowsGraphics_s = false;
411    }
412
413    //! Sets the language in the config-file back to the default.
414    void Core::resetLanguage()
415    {
416        ResetConfigValue(language_);
417    }
418
419    /**
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.
428    */
429    void Core::setThreadAffinity(int limitToCPU)
430    {
431#ifdef ORXONOX_PLATFORM_WINDOWS
432
433        if (limitToCPU <= 0)
434            return;
435
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
445
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
463    }
464
465    void Core::preUpdate(const Clock& time)
466    {
467        // Update singletons before general ticking
468        ScopedSingletonManager::preUpdate<ScopeID::Root>(time);
469        if (this->bGraphicsLoaded_)
470        {
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);
477        }
478        // Process console events and status line
479        if (this->ioConsole_ != NULL)
480            this->ioConsole_->preUpdate(time);
481        // Process thread commands
482        this->tclThreadManager_->preUpdate(time);
483    }
484
485    void Core::postUpdate(const Clock& time)
486    {
487        // Update singletons just before rendering
488        ScopedSingletonManager::postUpdate<ScopeID::Root>(time);
489        if (this->bGraphicsLoaded_)
490        {
491            // Update singletons just before rendering
492            ScopedSingletonManager::postUpdate<ScopeID::Graphics>(time);
493            // Render (doesn't throw)
494            this->graphicsManager_->postUpdate(time);
495        }
496    }
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    }
507
508
509    DevModeListener::DevModeListener()
510    {
511        RegisterRootObject(DevModeListener);
512    }
513}
Note: See TracBrowser for help on using the repository browser.