Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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