Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

adjusted the rest of the code to the new output system, but some changes have to be reviewed.
all output is currently printed with debug level.
compiles again (posix console untested)

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