Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/ai2/src/libraries/core/Core.cc @ 8804

Last change on this file since 8804 was 8505, checked in by rgrieder, 14 years ago

Do not limit the main thread to the first Core/CPU on Windows. It might slow down the game by a percent or two because Ogre forces the program to reside in the first core when it queries the time, hence potentially rescheduling the application.
On the other hand, limiting the application's main thread to one core has drawbacks when starting Orxonox multiple times without taking care of it by specifying —limitToCPU #.

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