Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

started implementing the GameStates. Not much for now, but most of the Orxonox.cc code has been copied to GSRoot, GSGraphics and GSLevel.
There is no level currently, but the main menu is shown. This is more of an svn save because I would really like to have Member ConsoleCommands.

  • 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.