Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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