Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Code related to config values for output is now implemented in BaseWriter.
There's a config value for the normal output level, a vector with additional contexts, and a config value for the level of these additional contexts.
ioconsole and standard console use the same values.

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