Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/environment/src/libraries/core/Core.cc @ 8474

Last change on this file since 8474 was 7624, checked in by rgrieder, 14 years ago

IOConsole does not seem to work properly with the Unix console on Mac OS X.
And the XCode console does not support the ANSI characters at all (how to check for XCode debugger at runtime?).

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