Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

added support for plugins in the buildsystem. plugins are like modules, but can be loaded/unloaded at runtime

  • Property svn:eol-style set to native
File size: 18.2 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/SignalHandler.h"
57#include "util/output/LogWriter.h"
58#include "util/output/OutputManager.h"
59#include "core/singleton/Scope.h"
60#include "ApplicationPaths.h"
61#include "ConfigurablePaths.h"
62#include "commandline/CommandLineIncludes.h"
63#include "config/ConfigFileManager.h"
64#include "GameMode.h"
65#include "GraphicsManager.h"
66#include "GUIManager.h"
67#include "Language.h"
68#include "Loader.h"
69#include "LuaState.h"
70#include "command/IOConsole.h"
71#include "command/TclBind.h"
72#include "command/TclThreadManager.h"
73#include "input/InputManager.h"
74#include "object/ObjectList.h"
75#include "module/DynLibManager.h"
76#include "module/ModuleInstance.h"
77#include "module/StaticInitializationManager.h"
78#include "CoreStaticInitializationHandler.h"
79#include "UpdateListener.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        : applicationPaths_(NULL)
97        , configurablePaths_(NULL)
98        , dynLibManager_(NULL)
99        , signalHandler_(NULL)
100        , configFileManager_(NULL)
101        , languageInstance_(NULL)
102        , loaderInstance_(NULL)
103        , ioConsole_(NULL)
104        , tclBind_(NULL)
105        , tclThreadManager_(NULL)
106        , rootScope_(NULL)
107        , graphicsManager_(NULL)
108        , inputManager_(NULL)
109        , guiManager_(NULL)
110        , graphicsScope_(NULL)
111        , bGraphicsLoaded_(false)
112        , staticInitHandler_(NULL)
113        , rootModule_(NULL)
114        , config_(NULL)
115        , destructionHelper_(this)
116    {
117        orxout(internal_status) << "initializing Core object..." << endl;
118
119        // Set the hard coded fixed paths
120        this->applicationPaths_ = new ApplicationPaths();
121
122        // Create a new dynamic library manager
123        this->dynLibManager_ = new DynLibManager();
124
125        // create handler for static initialization
126        new StaticInitializationManager(); // create singleton
127        this->staticInitHandler_ = new CoreStaticInitializationHandler();
128        StaticInitializationManager::getInstance().addHandler(this->staticInitHandler_);
129
130        // load root module (all libraries which are linked to the executable, including core, network, and orxonox)
131        this->rootModule_ = ModuleInstance::getCurrentModuleInstance();
132        StaticInitializationManager::getInstance().loadModule(this->rootModule_);
133
134        // Parse command line arguments AFTER the modules have been loaded (static code!)
135        CommandLineParser::parse(cmdLine);
136
137        // Set configurable paths like log, config and media
138        this->configurablePaths_ = new ConfigurablePaths();
139        this->configurablePaths_->setConfigurablePaths(ApplicationPaths::getInstance());
140
141        orxout(internal_info) << "Root path:       " << ApplicationPaths::getRootPathString() << endl;
142        orxout(internal_info) << "Executable path: " << ApplicationPaths::getExecutablePathString() << endl;
143        orxout(internal_info) << "Modules path:    " << ApplicationPaths::getModulePathString() << endl;
144        orxout(internal_info) << "Plugins path:    " << ApplicationPaths::getPluginPathString() << endl;
145
146        orxout(internal_info) << "Data path:       " << ConfigurablePaths::getDataPathString() << endl;
147        orxout(internal_info) << "Ext. data path:  " << ConfigurablePaths::getExternalDataPathString() << endl;
148        orxout(internal_info) << "Config path:     " << ConfigurablePaths::getConfigPathString() << endl;
149        orxout(internal_info) << "Log path:        " << ConfigurablePaths::getLogPathString() << endl;
150
151        // create a signal handler
152        // This call is placed as soon as possible, but after the directories are set
153        this->signalHandler_ = new SignalHandler();
154        this->signalHandler_->doCatch(ApplicationPaths::getExecutablePathString(), ConfigurablePaths::getLogPathString() + "orxonox_crash.log");
155
156#ifdef ORXONOX_PLATFORM_WINDOWS
157        // limit the main thread to the first core so that QueryPerformanceCounter doesn't jump
158        // do this after ogre has initialised. Somehow Ogre changes the settings again (not through
159        // the timer though).
160        int limitToCPU = CommandLineParser::getValue("limitToCPU");
161        if (limitToCPU > 0)
162            setThreadAffinity(static_cast<unsigned int>(limitToCPU));
163#endif
164
165        // Manage ini files and set the default settings file (usually orxonox.ini)
166        orxout(internal_info) << "Loading config:" << endl;
167        this->configFileManager_ = new ConfigFileManager();
168        this->configFileManager_->setFilename(ConfigFileType::Settings,
169            CommandLineParser::getValue("settingsFile").get<std::string>());
170
171        // Required as well for the config values
172        orxout(internal_info) << "Loading language:" << endl;
173        this->languageInstance_ = new Language();
174
175        // initialize root context
176        Context::setRootContext(new Context(NULL));
177
178        // Do this soon after the ConfigFileManager has been created to open up the
179        // possibility to configure everything below here
180        orxout(internal_info) << "configuring Core" << endl;
181        this->config_ = new CoreConfig();
182
183        // Set the correct log path and rewrite the log file with the correct log levels
184        OutputManager::getInstance().getLogWriter()->setLogDirectory(ConfigurablePaths::getLogPathString());
185
186#if !defined(ORXONOX_PLATFORM_APPLE) && !defined(ORXONOX_USE_WINMAIN)
187        // Create persistent IO console
188        if (CommandLineParser::getValue("noIOConsole").get<bool>() == false && this->config_->getStartIOConsole())
189        {
190            orxout(internal_info) << "creating IO console" << endl;
191            this->ioConsole_ = new IOConsole();
192        }
193#endif
194
195        // creates the class hierarchy for all classes with factories
196        orxout(internal_info) << "creating class hierarchy" << endl;
197        this->staticInitHandler_->initInstances(this->rootModule_);
198        this->staticInitHandler_->setInitInstances(true);
199
200        // Loader
201        this->loaderInstance_ = new Loader();
202
203        // Load OGRE excluding the renderer and the render window
204        orxout(internal_info) << "creating GraphicsManager:" << endl;
205        this->graphicsManager_ = new GraphicsManager(false);
206
207        // initialise Tcl
208        this->tclBind_ = new TclBind(ConfigurablePaths::getDataPathString());
209        this->tclThreadManager_ = new TclThreadManager(tclBind_->getTclInterpreter());
210
211        // Create singletons that always exist (in other libraries)
212        orxout(internal_info) << "creating root scope:" << endl;
213        this->rootScope_ = new Scope<ScopeID::ROOT>();
214
215        // Generate documentation instead of normal run?
216        std::string docFilename;
217        CommandLineParser::getValue("generateDoc", &docFilename);
218        if (!docFilename.empty())
219        {
220            std::ofstream docFile(docFilename.c_str());
221            if (docFile.is_open())
222            {
223                CommandLineParser::generateDoc(docFile);
224                docFile.close();
225            }
226            else
227                orxout(internal_error) << "Could not open file for documentation writing" << endl;
228        }
229
230        orxout(internal_status) << "finished initializing Core object" << endl;
231    }
232
233    void Core::destroy()
234    {
235        orxout(internal_status) << "destroying Core object..." << endl;
236
237        safeObjectDelete(&graphicsScope_);
238        safeObjectDelete(&guiManager_);
239        safeObjectDelete(&inputManager_);
240        safeObjectDelete(&graphicsManager_);
241        safeObjectDelete(&rootScope_);
242        safeObjectDelete(&tclThreadManager_);
243        safeObjectDelete(&tclBind_);
244        safeObjectDelete(&ioConsole_);
245        safeObjectDelete(&loaderInstance_);
246        safeObjectDelete(&config_);
247        safeObjectDelete(&languageInstance_);
248        safeObjectDelete(&configFileManager_);
249        safeObjectDelete(&signalHandler_);
250        Context::getRootContext()->unregisterObject(); // unregister context from object lists - otherwise the root context would be destroyed while unloading the root module
251        if (this->rootModule_)
252        {
253            StaticInitializationManager::getInstance().unloadModule(this->rootModule_);
254            this->rootModule_->deleteAllStaticallyInitializedInstances();
255        }
256        if (this->staticInitHandler_)
257            StaticInitializationManager::getInstance().removeHandler(this->staticInitHandler_);
258        Context::destroyRootContext();
259        safeObjectDelete(&rootModule_);
260        safeObjectDelete(&staticInitHandler_);
261        delete &StaticInitializationManager::getInstance();
262        safeObjectDelete(&dynLibManager_);
263        safeObjectDelete(&configurablePaths_);
264        safeObjectDelete(&applicationPaths_);
265
266        orxout(internal_status) << "finished destroying Core object" << endl;
267    }
268
269    void Core::loadModules()
270    {
271        orxout(internal_info) << "Loading modules:" << endl;
272
273        const std::vector<std::string>& modulePaths = ApplicationPaths::getInstance().getModulePaths();
274        for (std::vector<std::string>::const_iterator it = modulePaths.begin(); it != modulePaths.end(); ++it)
275        {
276            try
277            {
278                ModuleInstance* module = new ModuleInstance(*it);
279                this->loadModule(module);
280                this->modules_.push_back(module);
281            }
282            catch (...)
283            {
284                orxout(user_error) << "Couldn't load module \"" << *it << "\": " << Exception::handleMessage() << endl;
285            }
286        }
287
288        orxout(internal_info) << "finished loading modules" << endl;
289    }
290
291    void Core::loadModule(ModuleInstance* module)
292    {
293        orxout(internal_info) << "Loading module " << module->getName() << "..." << endl;
294
295        ModuleInstance::setCurrentModuleInstance(module);
296        DynLib* dynLib = this->dynLibManager_->load(module->getName());
297        module->setDynLib(dynLib);
298
299        StaticInitializationManager::getInstance().loadModule(module);
300    }
301
302    void Core::unloadModules()
303    {
304        for (std::list<ModuleInstance*>::iterator it = this->modules_.begin(); it != this->modules_.end(); ++it)
305        {
306            ModuleInstance* module = (*it);
307            this->unloadModule(module);
308            delete module;
309        }
310        this->modules_.clear();
311    }
312
313    void Core::unloadModule(ModuleInstance* module)
314    {
315        orxout(internal_info) << "Unloading module " << module->getName() << "..." << endl;
316
317        StaticInitializationManager::getInstance().unloadModule(module);
318
319        module->deleteAllStaticallyInitializedInstances();
320        this->dynLibManager_->unload(module->getDynLib());
321        module->setDynLib(NULL);
322    }
323
324    void Core::loadGraphics()
325    {
326        orxout(internal_info) << "loading graphics in Core" << endl;
327
328        // Any exception should trigger this, even in upgradeToGraphics (see its remarks)
329        Loki::ScopeGuard unloader = Loki::MakeObjGuard(*this, &Core::unloadGraphics, true);
330
331        // Upgrade OGRE to receive a render window
332        try
333        {
334            graphicsManager_->upgradeToGraphics();
335        }
336        catch (const InitialisationFailedException&)
337        {
338            // Exit the application if the Ogre config dialog was canceled
339            orxout(user_error) << Exception::handleMessage() << endl;
340            exit(EXIT_FAILURE);
341        }
342        catch (...)
343        {
344            // Recovery from this is very difficult. It requires to completely
345            // destroy Ogre related objects and load again (without graphics).
346            // However since Ogre 1.7 there seems to be a problem when Ogre
347            // throws an exception and the graphics engine then gets destroyed
348            // and reloaded between throw and catch (access violation in MSVC).
349            // That's why we abort completely and only display the exception.
350            orxout(user_error) << "An exception occurred during upgrade to graphics. "
351                               << "That is unrecoverable. The message was:" << endl
352                               << Exception::handleMessage() << endl;
353            abort();
354        }
355
356        // Calls the InputManager which sets up the input devices.
357        inputManager_ = new InputManager();
358
359        // Load the CEGUI interface
360        guiManager_ = new GUIManager(inputManager_->getMousePosition());
361
362        bGraphicsLoaded_ = true;
363        GameMode::bShowsGraphics_s = true;
364
365        // Load some sort of a debug overlay (only denoted by its name, "debug.oxo")
366        graphicsManager_->loadDebugOverlay();
367
368        // Create singletons associated with graphics (in other libraries)
369        orxout(internal_info) << "creating graphics scope:" << endl;
370        graphicsScope_ = new Scope<ScopeID::GRAPHICS>();
371
372        unloader.Dismiss();
373
374        orxout(internal_info) << "finished loading graphics in Core" << endl;
375    }
376
377    void Core::unloadGraphics(bool loadGraphicsManagerWithoutRenderer)
378    {
379        orxout(internal_info) << "unloading graphics in Core" << endl;
380
381        if (this->graphicsManager_)
382            this->graphicsManager_->unloadDebugOverlay();
383
384        safeObjectDelete(&graphicsScope_);
385        safeObjectDelete(&guiManager_);
386        safeObjectDelete(&inputManager_);
387        safeObjectDelete(&graphicsManager_);
388
389        // Load Ogre::Root again, but without the render system
390        try
391        {
392            if (loadGraphicsManagerWithoutRenderer)
393                this->graphicsManager_ = new GraphicsManager(false);
394        }
395        catch (...)
396        {
397            orxout(user_error) << "An exception occurred during 'unloadGraphics':" << Exception::handleMessage() << endl
398                               << "Another exception might be being handled which may lead to undefined behaviour!" << endl
399                               << "Terminating the program." << endl;
400            abort();
401        }
402
403        bGraphicsLoaded_ = false;
404        GameMode::bShowsGraphics_s = false;
405    }
406
407    /**
408    @note
409        The code of this function has been copied and adjusted from OGRE, an open source graphics engine.
410            (Object-oriented Graphics Rendering Engine)
411        For the latest info, see http://www.ogre3d.org/
412
413        Copyright (c) 2000-2008 Torus Knot Software Ltd
414
415        OGRE is licensed under the LGPL. For more info, see OGRE license.
416    */
417    void Core::setThreadAffinity(int limitToCPU)
418    {
419#ifdef ORXONOX_PLATFORM_WINDOWS
420
421        if (limitToCPU <= 0)
422            return;
423
424        unsigned int coreNr = limitToCPU - 1;
425        // Get the current process core mask
426        DWORD procMask;
427        DWORD sysMask;
428#  if _MSC_VER >= 1400 && defined (_M_X64)
429        GetProcessAffinityMask(GetCurrentProcess(), (PDWORD_PTR)&procMask, (PDWORD_PTR)&sysMask);
430#  else
431        GetProcessAffinityMask(GetCurrentProcess(), &procMask, &sysMask);
432#  endif
433
434        // If procMask is 0, consider there is only one core available
435        // (using 0 as procMask will cause an infinite loop below)
436        if (procMask == 0)
437            procMask = 1;
438
439        // if the core specified with coreNr is not available, take the lowest one
440        if (!(procMask & (1 << coreNr)))
441            coreNr = 0;
442
443        // Find the lowest core that this process uses and coreNr suggests
444        DWORD threadMask = 1;
445        while ((threadMask & procMask) == 0 || (threadMask < (1u << coreNr)))
446            threadMask <<= 1;
447
448        // Set affinity to the first core
449        SetThreadAffinityMask(GetCurrentThread(), threadMask);
450#endif
451    }
452
453    void Core::preUpdate(const Clock& time)
454    {
455        // Update UpdateListeners before general ticking
456        for (ObjectList<UpdateListener>::iterator it = ObjectList<UpdateListener>::begin(); it != ObjectList<UpdateListener>::end(); ++it)
457            it->preUpdate(time);
458        if (this->bGraphicsLoaded_)
459        {
460            // Process input events
461            this->inputManager_->preUpdate(time);
462            // Update GUI
463            this->guiManager_->preUpdate(time);
464        }
465        // Process console events and status line
466        if (this->ioConsole_ != NULL)
467            this->ioConsole_->preUpdate(time);
468        // Process thread commands
469        this->tclThreadManager_->preUpdate(time);
470    }
471
472    void Core::postUpdate(const Clock& time)
473    {
474        // Update UpdateListeners just before rendering
475        for (ObjectList<UpdateListener>::iterator it = ObjectList<UpdateListener>::begin(); it != ObjectList<UpdateListener>::end(); ++it)
476            it->postUpdate(time);
477        if (this->bGraphicsLoaded_)
478        {
479            // Render (doesn't throw)
480            this->graphicsManager_->postUpdate(time);
481        }
482    }
483}
Note: See TracBrowser for help on using the repository browser.