Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/libraries/core/Core.cc @ 8144

Last change on this file since 8144 was 8079, checked in by landauf, 14 years ago

merged usability branch back to trunk

incomplete summary of the changes in this branch:

  • enhanced keyboard navigation in GUIs
  • implemented new graphics menu and changeable window size at runtime
  • added developer mode
  • HUD shows if game is paused, game pauses if ingame menu is opened
  • removed a few obsolete commands and hid some that are more for internal use
  • numpad works in console and gui
  • faster loading of level info
  • enhanced usage of compositors (Shader class)
  • improved camera handling, configurable FOV and aspect ratio
  • Property svn:eol-style set to native
File size: 15.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    SetCommandLineSwitch(noIOConsole).information("Use this if you don't want to use the IOConsole (for instance for Lua debugging)");
84
85#ifdef ORXONOX_PLATFORM_WINDOWS
86    SetCommandLineArgument(limitToCPU, 1).information("Limits the program to one CPU/core (1, 2, 3, etc.). Default is the first core (faster than off)");
87#endif
88
89    Core::Core(const std::string& cmdLine)
90        // Cleanup guard for identifier destruction (incl. XMLPort, configValues, consoleCommands)
91        : identifierDestroyer_(Identifier::destroyAllIdentifiers)
92        // Cleanup guard for external console commands that don't belong to an Identifier
93        , consoleCommandDestroyer_(ConsoleCommand::destroyAll)
94        , bGraphicsLoaded_(false)
95        , bStartIOConsole_(true)
96        , lastLevelTimestamp_(0)
97        , ogreConfigTimestamp_(0)
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        // create persistent io console
159        if (CommandLineParser::getValue("noIOConsole").getBool())
160        {
161            ModifyConfigValue(bStartIOConsole_, tset, false);
162        }
163        if (this->bStartIOConsole_)
164            this->ioConsole_.reset(new IOConsole());
165
166        // creates the class hierarchy for all classes with factories
167        Identifier::createClassHierarchy();
168
169        // Load OGRE excluding the renderer and the render window
170        this->graphicsManager_.reset(new GraphicsManager(false));
171
172        // initialise Tcl
173        this->tclBind_.reset(new TclBind(PathConfig::getDataPathString()));
174        this->tclThreadManager_.reset(new TclThreadManager(tclBind_->getTclInterpreter()));
175
176        // Create singletons that always exist (in other libraries)
177        this->rootScope_.reset(new Scope<ScopeID::Root>());
178
179        // Generate documentation instead of normal run?
180        std::string docFilename;
181        CommandLineParser::getValue("generateDoc", &docFilename);
182        if (!docFilename.empty())
183        {
184            std::ofstream docFile(docFilename.c_str());
185            if (docFile.is_open())
186            {
187                CommandLineParser::generateDoc(docFile);
188                docFile.close();
189            }
190            else
191                COUT(0) << "Error: Could not open file for documentation writing" << endl;
192        }
193    }
194
195    /**
196    @brief
197        All destruction code is handled by scoped_ptrs and ScopeGuards.
198    */
199    Core::~Core()
200    {
201        // Remove us from the object lists again to avoid problems when destroying them
202        this->unregisterObject();
203    }
204
205    //! Function to collect the SetConfigValue-macro calls.
206    void Core::setConfigValues()
207    {
208#ifdef ORXONOX_RELEASE
209        const unsigned int defaultLevelLogFile = 3;
210        SetConfigValue(bDevMode_, false)
211            .description("Developer mode. If not set, hides some things from the user to not confuse him.");
212#else
213        const unsigned int defaultLevelLogFile = 4;
214        SetConfigValue(bDevMode_, true)
215            .description("Developer mode. If not set, hides some things from the user to not confuse him.");
216#endif
217        SetConfigValueExternal(softDebugLevelLogFile_, "OutputHandler", "softDebugLevelLogFile", defaultLevelLogFile)
218            .description("The maximum level of debug output shown in the log file");
219        OutputHandler::getInstance().setSoftDebugLevel(OutputHandler::logFileOutputListenerName_s, this->softDebugLevelLogFile_);
220
221        SetConfigValue(language_, Language::getInstance().defaultLanguage_)
222            .description("The language of the in game text")
223            .callback(this, &Core::languageChanged);
224        SetConfigValue(bInitRandomNumberGenerator_, true)
225            .description("If true, all random actions are different each time you start the game")
226            .callback(this, &Core::initRandomNumberGenerator);
227        SetConfigValue(bStartIOConsole_, true)
228            .description("Set to false if you don't want to use the IOConsole (for Lua debugging for instance)");
229        SetConfigValue(lastLevelTimestamp_, 0)
230            .description("Timestamp when the last level was started.");
231        SetConfigValue(ogreConfigTimestamp_, 0)
232            .description("Timestamp when the ogre config file was changed.");
233    }
234
235    //! Callback function if the language has changed.
236    void Core::languageChanged()
237    {
238        // Read the translation file after the language was configured
239        Language::getInstance().readTranslatedLanguageFile();
240    }
241
242    void Core::initRandomNumberGenerator()
243    {
244        static bool bInitialized = false;
245        if (!bInitialized && this->bInitRandomNumberGenerator_)
246        {
247            srand(static_cast<unsigned int>(time(0)));
248            rand();
249            bInitialized = true;
250        }
251    }
252
253    void Core::loadGraphics()
254    {
255        // Any exception should trigger this, even in upgradeToGraphics (see its remarks)
256        Loki::ScopeGuard unloader = Loki::MakeObjGuard(*this, &Core::unloadGraphics);
257
258        // Upgrade OGRE to receive a render window
259        try
260        {
261            graphicsManager_->upgradeToGraphics();
262        }
263        catch (const InitialisationFailedException&)
264        {
265            // Exit the application if the Ogre config dialog was canceled
266            COUT(1) << Exception::handleMessage() << std::endl;
267            exit(EXIT_FAILURE);
268        }
269        catch (...)
270        {
271            // Recovery from this is very difficult. It requires to completely
272            // destroy Ogre related objects and load again (without graphics).
273            // However since Ogre 1.7 there seems to be a problem when Ogre
274            // throws an exception and the graphics engine then gets destroyed
275            // and reloaded between throw and catch (access violation in MSVC).
276            // That's why we abort completely and only display the exception.
277            COUT(1) << "An exception occurred during upgrade to graphics. "
278                    << "That is unrecoverable. The message was:" << endl
279                    << Exception::handleMessage() << endl;
280            abort();
281        }
282
283        // Calls the InputManager which sets up the input devices.
284        inputManager_.reset(new InputManager());
285
286        // Load the CEGUI interface
287        guiManager_.reset(new GUIManager(inputManager_->getMousePosition()));
288
289        bGraphicsLoaded_ = true;
290        GameMode::bShowsGraphics_s = true;
291
292        // Load some sort of a debug overlay (only denoted by its name, "debug.oxo")
293        graphicsManager_->loadDebugOverlay();
294
295        // Create singletons associated with graphics (in other libraries)
296        graphicsScope_.reset(new Scope<ScopeID::Graphics>());
297
298        unloader.Dismiss();
299    }
300
301    void Core::unloadGraphics()
302    {
303        this->graphicsScope_.reset();
304        this->guiManager_.reset();
305        this->inputManager_.reset();
306        this->graphicsManager_.reset();
307
308        // Load Ogre::Root again, but without the render system
309        try
310            { this->graphicsManager_.reset(new GraphicsManager(false)); }
311        catch (...)
312        {
313            COUT(0) << "An exception occurred during 'unloadGraphics':" << Exception::handleMessage() << std::endl
314                    << "Another exception might be being handled which may lead to undefined behaviour!" << std::endl
315                    << "Terminating the program." << std::endl;
316            abort();
317        }
318
319        bGraphicsLoaded_ = false;
320        GameMode::bShowsGraphics_s = false;
321    }
322
323    //! Sets the language in the config-file back to the default.
324    void Core::resetLanguage()
325    {
326        ResetConfigValue(language_);
327    }
328
329    /**
330    @note
331        The code of this function has been copied and adjusted from OGRE, an open source graphics engine.
332            (Object-oriented Graphics Rendering Engine)
333        For the latest info, see http://www.ogre3d.org/
334
335        Copyright (c) 2000-2008 Torus Knot Software Ltd
336
337        OGRE is licensed under the LGPL. For more info, see OGRE license.
338    */
339    void Core::setThreadAffinity(int limitToCPU)
340    {
341#ifdef ORXONOX_PLATFORM_WINDOWS
342
343        if (limitToCPU <= 0)
344            return;
345
346        unsigned int coreNr = limitToCPU - 1;
347        // Get the current process core mask
348        DWORD procMask;
349        DWORD sysMask;
350#  if _MSC_VER >= 1400 && defined (_M_X64)
351        GetProcessAffinityMask(GetCurrentProcess(), (PDWORD_PTR)&procMask, (PDWORD_PTR)&sysMask);
352#  else
353        GetProcessAffinityMask(GetCurrentProcess(), &procMask, &sysMask);
354#  endif
355
356        // If procMask is 0, consider there is only one core available
357        // (using 0 as procMask will cause an infinite loop below)
358        if (procMask == 0)
359            procMask = 1;
360
361        // if the core specified with coreNr is not available, take the lowest one
362        if (!(procMask & (1 << coreNr)))
363            coreNr = 0;
364
365        // Find the lowest core that this process uses and coreNr suggests
366        DWORD threadMask = 1;
367        while ((threadMask & procMask) == 0 || (threadMask < (1u << coreNr)))
368            threadMask <<= 1;
369
370        // Set affinity to the first core
371        SetThreadAffinityMask(GetCurrentThread(), threadMask);
372#endif
373    }
374
375    void Core::preUpdate(const Clock& time)
376    {
377        // Update singletons before general ticking
378        ScopedSingletonManager::preUpdate<ScopeID::Root>(time);
379        if (this->bGraphicsLoaded_)
380        {
381            // Process input events
382            this->inputManager_->preUpdate(time);
383            // Update GUI
384            this->guiManager_->preUpdate(time);
385            // Update singletons before general ticking
386            ScopedSingletonManager::preUpdate<ScopeID::Graphics>(time);
387        }
388        // Process console events and status line
389        if (this->ioConsole_ != NULL)
390            this->ioConsole_->preUpdate(time);
391        // Process thread commands
392        this->tclThreadManager_->preUpdate(time);
393    }
394
395    void Core::postUpdate(const Clock& time)
396    {
397        // Update singletons just before rendering
398        ScopedSingletonManager::postUpdate<ScopeID::Root>(time);
399        if (this->bGraphicsLoaded_)
400        {
401            // Update singletons just before rendering
402            ScopedSingletonManager::postUpdate<ScopeID::Graphics>(time);
403            // Render (doesn't throw)
404            this->graphicsManager_->postUpdate(time);
405        }
406    }
407
408    void Core::updateLastLevelTimestamp()
409    {
410        ModifyConfigValue(lastLevelTimestamp_, set, static_cast<long long>(time(NULL)));
411    }
412
413    void Core::updateOgreConfigTimestamp()
414    {
415        ModifyConfigValue(ogreConfigTimestamp_, set, static_cast<long long>(time(NULL)));
416    }
417}
Note: See TracBrowser for help on using the repository browser.