Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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