Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/core7/src/libraries/core/Core.cc @ 10413

Last change on this file since 10413 was 10413, checked in by landauf, 10 years ago

use the generic UpdateListener interface to receive calls to preUpdate() and postUpdate() instead of limiting this functionality to singletons.

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