Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/gui/src/orxonox/Orxonox.cc @ 1654

Last change on this file since 1654 was 1654, checked in by rgrieder, 16 years ago

forgot getSingleton in Orxonox.cc
There are now only three classes uses getSingleton: ConfigFileManager, Server and Client

  • Property svn:eol-style set to native
File size: 17.1 KB
RevLine 
[1038]1/*
[1293]2 *   ORXONOX - the hottest 3D action shooter ever to exist
[1056]3 *                    > www.orxonox.net <
[1038]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:
[1535]23 *      Reto Grieder
[1638]24 *      Benjamin Knecht <beni_at_orxonox.net>, (C) 2007
[1535]25 *   Co-authors:
[1638]26 *      ...
[1038]27 *
28 */
29
30/**
31 @file
32 @brief Orxonox Main Class
33 */
34
35// Precompiled Headers
36#include "OrxonoxStableHeaders.h"
[1039]37#include "Orxonox.h"
[1038]38
39//****** STD *******
40#include <deque>
[1638]41#include <cassert>
[1038]42
43//****** OGRE ******
44#include <OgreFrameListener.h>
45#include <OgreOverlay.h>
46#include <OgreOverlayManager.h>
47#include <OgreRoot.h>
48#include <OgreTimer.h>
49#include <OgreWindowEventUtilities.h>
50
51//***** ORXONOX ****
52// util
53//#include "util/Sleep.h"
54
[1293]55// core
[1054]56#include "core/ConfigFileManager.h"
[1638]57#include "core/ConfigValueIncludes.h"
[1293]58#include "core/ConsoleCommand.h"
[1038]59#include "core/Debug.h"
60#include "core/Loader.h"
[1638]61#include "core/Exception.h"
[1535]62#include "core/input/InputManager.h"
[1638]63#include "core/input/SimpleInputState.h"
64#include "core/input/KeyBinder.h"
[1293]65#include "core/TclBind.h"
[1535]66#include "core/Core.h"
[1038]67
68// audio
69#include "audio/AudioManager.h"
70
71// network
72#include "network/Server.h"
73#include "network/Client.h"
74
75// objects and tools
[1625]76#include "overlays/console/InGameConsole.h"
[1535]77#include "objects/Tickable.h"
[1608]78#include "objects/Backlight.h"
[1563]79#include "tools/ParticleInterface.h"
[1638]80#include "gui/GUIManager.h"
[1038]81
[1502]82#include "GraphicsEngine.h"
[1535]83#include "Settings.h"
[1625]84#include "Radar.h"
[1502]85
[1038]86// globals for the server or client
[1577]87static network::Client *client_g = 0;
88static network::Server *server_g = 0;
[1038]89
90namespace orxonox
[1293]91{
[1638]92  SetConsoleCommand(Orxonox, exit, true).setKeybindMode(KeybindMode::OnPress);
93  SetConsoleCommand(Orxonox, slomo, true).setAccessLevel(AccessLevel::Offline).setDefaultValue(0, 1.0).setAxisParamIndex(0).setIsAxisRelative(false);
94  SetConsoleCommand(Orxonox, setTimeFactor, true).setAccessLevel(AccessLevel::Offline).setDefaultValue(0, 1.0);
95  SetConsoleCommand(Orxonox, loadGame, true).setAccessLevel(AccessLevel::User).setDefaultValue(0, "standalone");
[1505]96
[1038]97  /**
98    @brief Reference to the only instance of the class.
99  */
100  Orxonox *Orxonox::singletonRef_s = 0;
101
102  /**
[1293]103   * Create a new instance of Orxonox. Avoid doing any actual work here.
[1038]104   */
[1625]105  Orxonox::Orxonox()
[1642]106    : startLevel_(0)
[1625]107    , hud_(0)
108    //, auMan_(0)
109    , timer_(0)
110    , bAbort_(false)
111    , timefactor_(1.0f)
[1638]112    , mode_(GameMode::GM_Unspecified)
113    , debugRefreshTime_(0.0f)
[1653]114    , graphicsEngine_(0)
[1642]115    , inputManager_(0)
116    , radar_(0)
[1646]117    , console_(0)
118    , guiManager_(0)
[1038]119  {
[1638]120    RegisterRootObject(Orxonox);
121
[1642]122    //assert(singletonRef_s == 0);
123    OrxAssert(singletonRef_s == 0, "asdfasdfasdfasdfblahblah");
[1638]124    singletonRef_s = this;
[1038]125  }
126
127  /**
[1293]128   * Destruct Orxonox.
[1038]129   */
130  Orxonox::~Orxonox()
131  {
132    // keep in mind: the order of deletion is very important!
[1642]133    if (this->timer_)
134      delete this->timer_;
135
136    Loader::unload(startLevel_);
[1625]137    if (this->startLevel_)
138      delete this->startLevel_;
139
140    Loader::unload(hud_);
141    if (this->hud_)
142      delete this->hud_;
143
144    if (this->radar_)
145      delete this->radar_;
146
[1646]147    if (this->guiManager_)
148      delete guiManager_;
149
[1293]150    //if (this->auMan_)
151    //  delete this->auMan_;
[1038]152
[1642]153    if (this->console_)
154      delete this->console_;
155
156    if (inputManager_)
157      delete inputManager_;
158
[1653]159    if (this->graphicsEngine_)
160      delete graphicsEngine_;
[1638]161
[1293]162    if (network::Client::getSingleton())
163      network::Client::destroySingleton();
[1038]164    if (server_g)
[1534]165      delete network::Server::getSingleton();
[1638]166
[1642]167    // this call will delete every BaseObject!
168    // But currently this will call methods of objects that exist no more
169    // The only 'memory leak' is the ParticleSpawer. They would be deleted here
170    // and call a sceneNode method that has already been destroy by the corresponding space ship.
171    //Loader::close();
172
[1638]173    singletonRef_s = 0;
[1038]174  }
175
[1638]176  void Orxonox::setConfigValues()
177  {
178    SetConfigValue(debugRefreshTime_, 0.2).description("Sets the time interval at which average fps, etc. get updated.");
179  }
[1038]180
[1638]181
[1038]182  /**
183    Asks the mainloop nicely to abort.
184  */
185  void Orxonox::abortRequest()
186  {
[1293]187    COUT(3) << "Orxonox: Abort requested." << std::endl;
[1038]188    bAbort_ = true;
189  }
190
191  /**
[1293]192   * @return singleton reference
[1038]193   */
[1654]194  Orxonox& Orxonox::getInstance()
[1038]195  {
[1638]196    assert(singletonRef_s);
197    return *singletonRef_s;
[1038]198  }
199
200  /**
[1563]201    @brief Changes the speed of Orxonox
202  */
203  void Orxonox::setTimeFactor(float factor)
204  {
[1654]205    float change = factor / Orxonox::getInstance().getTimeFactor();
206    Orxonox::getInstance().timefactor_ = factor;
[1563]207    for (Iterator<ParticleInterface> it = ObjectList<ParticleInterface>::begin(); it; ++it)
208        it->setSpeedFactor(it->getSpeedFactor() * change);
[1608]209
210    for (Iterator<Backlight> it = ObjectList<Backlight>::begin(); it; ++it)
[1654]211        it->setTimeFactor(Orxonox::getInstance().getTimeFactor());
[1563]212  }
213
[1653]214    /**
215    @brief
216        Starts the whole Game.
217    @param path
218        path to config (in home dir or something)
219    */
220    void Orxonox::start()
221    {
222#if ORXONOX_DEBUG_MODE == 1
223        ConfigFileManager::getSingleton()->setFile(CFT_Settings, "orxonox_d.ini");
[1535]224#else
[1653]225        ConfigFileManager::getSingleton()->setFile(CFT_Settings, "orxonox.ini");
[1535]226#endif
227
[1653]228        // creates the class hierarchy for all classes with factories
229        Factory::createClassHierarchy();
[1038]230
[1653]231        setConfigValues();
[1293]232
[1653]233        const Settings::CommandLineArgument* mode = Settings::getCommandLineArgument("mode");
234        assert(mode);
235        if (!mode->bHasDefaultValue_)
236        {
237            Settings::setGameMode(mode->value_);
238            this->mode_ = Settings::getGameMode();
239        }
240        COUT(3) << "Orxonox: Game mode is " << mode_.name << "." << std::endl;
[1038]241
[1653]242        const Settings::CommandLineArgument* dataPath = Settings::getCommandLineArgument("dataPath");
243        assert(dataPath);
244        if (!dataPath->bHasDefaultValue_)
245        {
246            if (*dataPath->value_.getString().end() != '/' && *dataPath->value_.getString().end() != '\\')
247                Settings::tsetDataPath(dataPath->value_.getString() + "/");
248            else
249                Settings::tsetDataPath(dataPath->value_.getString());
250        }
[1535]251
[1653]252        try
[1638]253        {
[1653]254            // initialise TCL
255            TclBind::getInstance().setDataPath(Settings::getDataPath());
[1052]256
[1653]257            graphicsEngine_ = new GraphicsEngine();
258            graphicsEngine_->setup();       // creates ogre root and other essentials
[1052]259
[1653]260            if (mode_.showsGraphics)
261            {
262                graphicsEngine_->loadRenderer();    // creates the render window
[1052]263
[1653]264                // TODO: Spread this so that this call only initialises things needed for the Console and GUI
265                graphicsEngine_->initialiseResources();
[1052]266
[1653]267                // Calls the InputManager which sets up the input devices.
268                // The render window width and height are used to set up the mouse movement.
269                inputManager_ = new InputManager();
270                inputManager_->initialise(graphicsEngine_->getWindowHandle(),
271                    graphicsEngine_->getWindowWidth(), graphicsEngine_->getWindowHeight(), true, true, true);
272                KeyBinder* keyBinder = new KeyBinder();
273                keyBinder->loadBindings();
274                inputManager_->createInputState<SimpleInputState>("game", 20)->setHandler(keyBinder);
[1638]275
[1653]276                // Load the InGameConsole
277                console_ = new InGameConsole();
278                console_->initialise();
[1638]279
[1653]280                // load the CEGUI interface
281                guiManager_ = new GUIManager();
282                guiManager_->initialise();
283            }
284            else
285            {
286                // TODO: Initialise a not yet written console that operates in the shell we
287                // started the game from.
288                // We probably want to use std::cin to catch input (OIS uses DirectX or X server)
289            }
290
291            bool showGUI = true;
292            if (mode_.mode != GameMode::Unspecified)
293            {
294                showGUI = false;
295                // a game mode was specified with the command line
296                // we therefore load the game and level directly
297
298                if (!loadLevel(this->mode_))
299                {
300                    COUT(1) << "Loading with predefined mode failed. Showing main menu." << std::endl;
301                    showGUI = true;
302                    mode_ = GameMode::GM_Unspecified;
303                }
304            }
305
306            if (showGUI)
307            {
308                // show main menu
309                GUIManager::getInstance().showGUI("MainMenu", 0);
310                GraphicsEngine::getInstance().getViewport()->setCamera(GUIManager::getInstance().getCamera());
311            }
[1638]312        }
[1653]313        catch (std::exception& ex)
[1638]314        {
[1653]315            COUT(1) << ex.what() << std::endl;
316            COUT(1) << "Loading sequence aborted." << std::endl;
317            return;
[1638]318        }
[1293]319
[1653]320        modeRequest_ = mode_;
321        // here happens the game
322        startRenderLoop();
[1577]323
[1653]324        if (mode_.mode == GameMode::Client)
325            network::Client::getSingleton()->closeConnection();
[1577]326
[1653]327        if (mode_.hasServer)
328            server_g->close();
329    }
[1293]330
[1653]331    /*static*/ void Orxonox::loadGame(const std::string& name)
332    {
333        const GameMode& mode = Settings::getGameMode(name);
334        if (mode.mode == GameMode::None)
335            return;
[1293]336
[1654]337        getInstance().modeRequest_ = mode;
[1653]338    }
[1293]339
[1653]340    bool Orxonox::loadLevel(const GameMode& mode)
341    {
342        bool success = true;
[1293]343
[1653]344        if (mode.showsGraphics)
345        {
346            // create Ogre SceneManager for the level
347            graphicsEngine_->createNewScene();
[1293]348
[1653]349            if (!loadPlayground())
350                return false;
351        }
[1293]352
[1653]353        switch (mode.mode)
354        {
355        case GameMode::Server:
356            success &= serverLoad();
357            break;
358        case GameMode::Client:
359            success &= clientLoad();
360            break;
361        case GameMode::Dedicated:
362            success &= serverLoad();
363            break;
364        case GameMode::Standalone:
365            success &= standaloneLoad();
366            break;
367        default: // never happens
368            assert(false);
369        }
[1293]370
[1653]371        if (success)
372        {
373            InputManager::getInstance().requestEnterState("game");
374            this->mode_ = mode;
375        }
[1052]376
[1653]377        return success;
378    }
379
[1293]380  /**
381   * Loads everything in the scene except for the actual objects.
[1577]382   * This includes HUD, audio..
[1293]383   */
384  bool Orxonox::loadPlayground()
385  {
[1638]386    // Start the Radar
387    this->radar_ = new Radar();
[1052]388
[1293]389    // Load the HUD
[1625]390    COUT(3) << "Orxonox: Loading HUD" << std::endl;
391    hud_ = new Level(Settings::getDataPath() + "overlay/hud.oxo");
[1638]392    return Loader::load(hud_);
393  }
[1577]394
[1638]395  /**
396   * Helper method to load a level.
397   */
398  bool Orxonox::loadScene()
399  {
400    COUT(0) << "Loading level..." << std::endl;
401    startLevel_ = new Level(Settings::getDataPath() + "levels/sample.oxw");
402    return Loader::open(startLevel_);
[1293]403  }
[1052]404
[1293]405  /**
406   * Level loading method for server mode.
407   */
408  bool Orxonox::serverLoad()
409  {
[1577]410    COUT(0) << "Loading level in server mode" << std::endl;
[1052]411
[1638]412    assert(Settings::getCommandLineArgument("port"));
413    int serverPort = Settings::getCommandLineArgument("port")->value_;
[1534]414    //server_g = new network::Server(serverPort_);
[1638]415    server_g = network::Server::createSingleton(serverPort);
[1052]416
[1293]417    if (!loadScene())
418      return false;
[1052]419
[1038]420    server_g->open();
[1052]421
[1293]422    return true;
[1038]423  }
[1052]424
[1293]425  /**
426   * Level loading method for client mode.
427   */
428  bool Orxonox::clientLoad()
429  {
[1577]430    COUT(0) << "Loading level in client mode" << std::endl;\
[1052]431
[1638]432    assert(Settings::getCommandLineArgument("port"));
433    assert(Settings::getCommandLineArgument("ip"));
434    int serverPort = Settings::getCommandLineArgument("port")->value_;
435    std::string serverIP = Settings::getCommandLineArgument("ip")->value_;
436
437    if (serverIP.compare("") == 0)
[1293]438      client_g = network::Client::createSingleton();
439    else
[1638]440      client_g = network::Client::createSingleton(serverIP, serverPort);
[1293]441
[1502]442    if(!client_g->establishConnection())
443      return false;
[1293]444    client_g->tick(0);
445
446    return true;
[1038]447  }
448
[1293]449  /**
450   * Level loading method for standalone mode.
451   */
452  bool Orxonox::standaloneLoad()
[1038]453  {
[1577]454    COUT(0) << "Loading level in standalone mode" << std::endl;
[1038]455
[1293]456    if (!loadScene())
457      return false;
[1038]458
[1293]459    return true;
[1038]460  }
461
462  /**
463    Main loop of the orxonox game.
[1638]464    We use the Ogre::Timer to measure time since it uses the most precise
465    method an a platform (however the windows timer lacks time when under
466    heavy kernel load!).
467    There is a simple mechanism to measure the average time spent in our
468    ticks as it may indicate performance issues.
469    A note about the Ogre::FrameListener: Even though we don't use them,
470    they still get called. However, the delta times are not correct (except
471    for timeSinceLastFrame, which is the most important). A little research
472    as shown that there is probably only one FrameListener that doesn't even
473    need the time. So we shouldn't run into problems.
[1038]474  */
[1638]475  void Orxonox::startRenderLoop()
[1038]476  {
[1120]477    Ogre::Root& ogreRoot = Ogre::Root::getSingleton();
[1038]478
479    // use the ogre timer class to measure time.
480    if (!timer_)
481      timer_ = new Ogre::Timer();
482
[1625]483    unsigned long frameCount = 0;
484   
[1645]485    const unsigned long refreshTime = (unsigned long)(debugRefreshTime_ * 1000000.0f);
[1625]486    unsigned long refreshStartTime = 0;
487    unsigned long tickTime = 0;
488    unsigned long oldFrameCount = 0;
[1502]489
[1625]490    unsigned long timeBeforeTick = 0;
491    unsigned long timeBeforeTickOld = 0;
492    unsigned long timeAfterTick = 0;
[1563]493
[1638]494    // TODO: Update time in seconds every 7 seconds to avoid any overflow (7 secs is very tight)
495
[1625]496    COUT(3) << "Orxonox: Starting the main loop." << std::endl;
[1502]497
[1638]498    try
499    {
[1625]500    timer_->reset();
[1502]501    while (!bAbort_)
502    {
[1638]503        // get current time
504        timeBeforeTickOld = timeBeforeTick;
505        timeBeforeTick    = timer_->getMicroseconds();
506        float dt = (timeBeforeTick - timeBeforeTickOld) / 1000000.0;
[1038]507
[1638]508        // check whether we have to load a game
509        if (mode_.mode != modeRequest_.mode && mode_.mode == GameMode::Unspecified)
510        {
511            this->loadLevel(modeRequest_);
512            this->modeRequest_ = GameMode::GM_None;
513        }
[1038]514
515
[1638]516        // tick the core (needs real time for input and tcl thread management)
517        Core::tick(dt);
[1625]518
[1638]519        // Call those objects that need the real time
520        for (Iterator<TickableReal> it = ObjectList<TickableReal>::start(); it; ++it)
521          it->tick(dt);
522        // Call the scene objects
523        for (Iterator<Tickable> it = ObjectList<Tickable>::start(); it; ++it)
524          it->tick(dt * this->timefactor_);
[1038]525
[1638]526        // call server/client with normal dt
527        if (client_g)
528          client_g->tick(dt * this->timefactor_);
529        if (server_g)
530          server_g->tick(dt * this->timefactor_);
[1625]531
532
[1638]533        // get current time once again
534        timeAfterTick = timer_->getMicroseconds();
[1625]535
[1638]536        tickTime += timeAfterTick - timeBeforeTick;
537        if (timeAfterTick > refreshStartTime + refreshTime)
538        {
[1653]539          GraphicsEngine::getInstance().setAverageTickTime(
[1638]540              (float)tickTime * 0.001 / (frameCount - oldFrameCount));
541          float avgFPS = (float)(frameCount - oldFrameCount) / (timeAfterTick - refreshStartTime) * 1000000.0;
[1653]542          GraphicsEngine::getInstance().setAverageFramesPerSecond(avgFPS);
[1625]543
[1638]544          oldFrameCount = frameCount;
545          tickTime = 0;
546          refreshStartTime = timeAfterTick;
547        }
[1502]548
[1638]549        // don't forget to call _fireFrameStarted in ogre to make sure
550        // everything goes smoothly
551        Ogre::FrameEvent evt;
552        evt.timeSinceLastFrame = dt;
553        evt.timeSinceLastEvent = dt; // note: same time, but shouldn't matter anyway
554        ogreRoot._fireFrameStarted(evt);
[1293]555
[1638]556        if (mode_.showsGraphics)
557        {
558          // Pump messages in all registered RenderWindows
559          // This calls the WindowEventListener objects.
560          Ogre::WindowEventUtilities::messagePump();
561          // make sure the window stays active even when not focused
562          // (probably only necessary on windows)
[1653]563          GraphicsEngine::getInstance().setWindowActivity(true);
[1502]564
[1638]565          // tick CEGUI
566          GUIManager::getInstance().tick(dt);
567
568          // render
569          ogreRoot._updateAllRenderTargets();
570        }
571
572        // again, just to be sure ogre works fine
573        ogreRoot._fireFrameEnded(evt); // note: uses the same time as _fireFrameStarted
574
575        ++frameCount;
576    }
577    }
578    catch (std::exception& ex)
579    {
580      // something went wrong.
581      COUT(1) << ex.what() << std::endl;
582      COUT(1) << "Main loop was stopped by an unhandled exception. Shutting down." << std::endl;
583    }
[1038]584  }
585}
Note: See TracBrowser for help on using the repository browser.