Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Added CommandLine class.
You can now call SetCommandLineArgument like SetConsoleCommand and hereby define a new command line argument. They are passed in main() and then they can be accessed by commandLine::getCommandLineArgument().

  • Property svn:eol-style set to native
File size: 14.5 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    , debugRefreshTime_(0.0f)
111    , graphicsEngine_(0)
112    , inputManager_(0)
113    , radar_(0)
114    , console_(0)
115    , guiManager_(0)
116  {
117    RegisterRootObject(Orxonox);
118
119    //assert(singletonRef_s == 0);
120    OrxAssert(singletonRef_s == 0, "Cannot instantiate Orxonox class twice");
121    singletonRef_s = this;
122  }
123
124  /**
125   * Destruct Orxonox.
126   */
127  Orxonox::~Orxonox()
128  {
129    // keep in mind: the order of deletion is very important!
130    if (this->timer_)
131      delete this->timer_;
132
133    Loader::unload(startLevel_);
134    if (this->startLevel_)
135      delete this->startLevel_;
136
137    Loader::unload(hud_);
138    if (this->hud_)
139      delete this->hud_;
140
141    if (this->radar_)
142      delete this->radar_;
143
144    if (this->guiManager_)
145      delete guiManager_;
146
147    //if (this->auMan_)
148    //  delete this->auMan_;
149
150    if (this->console_)
151      delete this->console_;
152
153    if (inputManager_)
154      delete inputManager_;
155
156    if (this->graphicsEngine_)
157      delete graphicsEngine_;
158
159    if (network::Client::getSingleton())
160      network::Client::destroySingleton();
161    if (server_g)
162      delete network::Server::getSingleton();
163
164    // this call will delete every BaseObject!
165    // But currently this will call methods of objects that exist no more
166    // The only 'memory leak' is the ParticleSpawer. They would be deleted here
167    // and call a sceneNode method that has already been destroy by the corresponding space ship.
168    //Loader::close();
169
170    singletonRef_s = 0;
171  }
172
173  void Orxonox::setConfigValues()
174  {
175    SetConfigValue(debugRefreshTime_, 0.2).description("Sets the time interval at which average fps, etc. get updated.");
176  }
177
178
179  /**
180    Asks the mainloop nicely to abort.
181  */
182  void Orxonox::abortRequest()
183  {
184    COUT(3) << "Orxonox: Abort requested." << std::endl;
185    bAbort_ = true;
186  }
187
188  /**
189   * @return singleton reference
190   */
191  Orxonox& Orxonox::getInstance()
192  {
193    assert(singletonRef_s);
194    return *singletonRef_s;
195  }
196
197  /**
198    @brief Changes the speed of Orxonox
199  */
200  void Orxonox::setTimeFactor(float factor)
201  {
202    float change = factor / Orxonox::getInstance().getTimeFactor();
203    Orxonox::getInstance().timefactor_ = factor;
204    for (Iterator<ParticleInterface> it = ObjectList<ParticleInterface>::begin(); it; ++it)
205        it->setSpeedFactor(it->getSpeedFactor() * change);
206
207    for (Iterator<Backlight> it = ObjectList<Backlight>::begin(); it; ++it)
208        it->setTimeFactor(Orxonox::getInstance().getTimeFactor());
209  }
210
211    /**
212    @brief
213        Starts the whole Game.
214    @param path
215        path to config (in home dir or something)
216    */
217    void Orxonox::start()
218    {
219#if ORXONOX_DEBUG_MODE == 1
220        ConfigFileManager::getSingleton()->setFile(CFT_Settings, "orxonox_d.ini");
221#else
222        ConfigFileManager::getSingleton()->setFile(CFT_Settings, "orxonox.ini");
223#endif
224
225        // creates the class hierarchy for all classes with factories
226        Factory::createClassHierarchy();
227
228        setConfigValues();
229
230        //const Settings::CommandLineArgument* dataPath = Settings::getCommandLineArgument("dataPath");
231        //assert(dataPath);
232        //if (!dataPath->bHasDefaultValue_)
233        //{
234        //    if (*dataPath->value_.getString().end() != '/' && *dataPath->value_.getString().end() != '\\')
235        //        Settings::tsetDataPath(dataPath->value_.getString() + "/");
236        //    else
237        //        Settings::tsetDataPath(dataPath->value_.getString());
238        //}
239
240        try
241        {
242            // initialise TCL
243            TclBind::getInstance().setDataPath(Settings::getDataPath());
244
245            graphicsEngine_ = new GraphicsEngine();
246            graphicsEngine_->setup();       // creates ogre root and other essentials
247
248            if (Settings::showsGraphics())
249            {
250                graphicsEngine_->loadRenderer();    // creates the render window
251
252                // TODO: Spread this so that this call only initialises things needed for the Console and GUI
253                graphicsEngine_->initialiseResources();
254
255                // Calls the InputManager which sets up the input devices.
256                // The render window width and height are used to set up the mouse movement.
257                inputManager_ = new InputManager();
258                inputManager_->initialise(graphicsEngine_->getWindowHandle(),
259                    graphicsEngine_->getWindowWidth(), graphicsEngine_->getWindowHeight(), true);
260                KeyBinder* keyBinder = new KeyBinder();
261                keyBinder->loadBindings();
262                inputManager_->createInputState<SimpleInputState>("game", 20)->setHandler(keyBinder);
263
264                // Load the InGameConsole
265                console_ = new InGameConsole();
266                console_->initialise();
267
268                // load the CEGUI interface
269                guiManager_ = new GUIManager();
270                guiManager_->initialise();
271            }
272            else
273            {
274                // TODO: Initialise a not yet written console that operates in the shell we
275                // started the game from.
276                // We probably want to use std::cin to catch input (OIS uses DirectX or X server)
277            }
278        }
279        catch (std::exception& ex)
280        {
281            COUT(1) << ex.what() << std::endl;
282            COUT(1) << "Loading sequence aborted." << std::endl;
283            return;
284        }
285
286        // here happens the game
287        startRenderLoop();
288
289        //if (mode_.mode == GameMode::Client)
290        //    network::Client::getSingleton()->closeConnection();
291
292        //if (mode_.hasServer)
293        //    server_g->close();
294    }
295
296  /**
297   * Loads everything in the scene except for the actual objects.
298   * This includes HUD, audio..
299   */
300  bool Orxonox::loadPlayground()
301  {
302    // Start the Radar
303    this->radar_ = new Radar();
304
305    // Load the HUD
306    COUT(3) << "Orxonox: Loading HUD" << std::endl;
307    hud_ = new Level(Settings::getDataPath() + "overlay/hud.oxo");
308    return Loader::load(hud_);
309  }
310
311  /**
312   * Helper method to load a level.
313   */
314  bool Orxonox::loadScene()
315  {
316    COUT(0) << "Loading level..." << std::endl;
317    startLevel_ = new Level(Settings::getDataPath() + "levels/sample.oxw");
318    return Loader::open(startLevel_);
319  }
320
321  /**
322   * Level loading method for server mode.
323   */
324  bool Orxonox::serverLoad()
325  {
326    COUT(0) << "Loading level in server mode" << std::endl;
327
328    //assert(Settings::getCommandLineArgument("port"));
329    //int serverPort = Settings::getCommandLineArgument("port")->value_;
330    ////server_g = new network::Server(serverPort_);
331    //server_g = network::Server::createSingleton(serverPort);
332
333    if (!loadScene())
334      return false;
335
336    server_g->open();
337
338    return true;
339  }
340
341  /**
342   * Level loading method for client mode.
343   */
344  bool Orxonox::clientLoad()
345  {
346    COUT(0) << "Loading level in client mode" << std::endl;\
347
348    //assert(Settings::getCommandLineArgument("port"));
349    //assert(Settings::getCommandLineArgument("ip"));
350    //int serverPort = Settings::getCommandLineArgument("port")->value_;
351    //std::string serverIP = Settings::getCommandLineArgument("ip")->value_;
352
353    //if (serverIP.compare("") == 0)
354    //  client_g = network::Client::createSingleton();
355    //else
356    //  client_g = network::Client::createSingleton(serverIP, serverPort);
357
358    if(!client_g->establishConnection())
359      return false;
360    client_g->tick(0);
361
362    return true;
363  }
364
365  /**
366   * Level loading method for standalone mode.
367   */
368  bool Orxonox::standaloneLoad()
369  {
370    COUT(0) << "Loading level in standalone mode" << std::endl;
371
372    if (!loadScene())
373      return false;
374
375    return true;
376  }
377
378  /**
379    Main loop of the orxonox game.
380    We use the Ogre::Timer to measure time since it uses the most precise
381    method an a platform (however the windows timer lacks time when under
382    heavy kernel load!).
383    There is a simple mechanism to measure the average time spent in our
384    ticks as it may indicate performance issues.
385    A note about the Ogre::FrameListener: Even though we don't use them,
386    they still get called. However, the delta times are not correct (except
387    for timeSinceLastFrame, which is the most important). A little research
388    as shown that there is probably only one FrameListener that doesn't even
389    need the time. So we shouldn't run into problems.
390  */
391  void Orxonox::startRenderLoop()
392  {
393    Ogre::Root& ogreRoot = Ogre::Root::getSingleton();
394
395    // use the ogre timer class to measure time.
396    if (!timer_)
397      timer_ = new Ogre::Timer();
398
399    unsigned long frameCount = 0;
400   
401    const unsigned long refreshTime = (unsigned long)(debugRefreshTime_ * 1000000.0f);
402    unsigned long refreshStartTime = 0;
403    unsigned long tickTime = 0;
404    unsigned long oldFrameCount = 0;
405
406    unsigned long timeBeforeTick = 0;
407    unsigned long timeBeforeTickOld = 0;
408    unsigned long timeAfterTick = 0;
409
410    // TODO: Update time in seconds every 7 seconds to avoid any overflow (7 secs is very tight)
411
412    COUT(3) << "Orxonox: Starting the main loop." << std::endl;
413
414    try
415    {
416    timer_->reset();
417    while (!bAbort_)
418    {
419        // get current time
420        timeBeforeTickOld = timeBeforeTick;
421        timeBeforeTick    = timer_->getMicroseconds();
422        float dt = (timeBeforeTick - timeBeforeTickOld) / 1000000.0;
423
424
425        // tick the core (needs real time for input and tcl thread management)
426        Core::tick(dt);
427
428        // Call those objects that need the real time
429        for (Iterator<TickableReal> it = ObjectList<TickableReal>::start(); it; ++it)
430          it->tick(dt);
431        // Call the scene objects
432        for (Iterator<Tickable> it = ObjectList<Tickable>::start(); it; ++it)
433          it->tick(dt * this->timefactor_);
434
435        // call server/client with normal dt
436        if (client_g)
437          client_g->tick(dt * this->timefactor_);
438        if (server_g)
439          server_g->tick(dt * this->timefactor_);
440
441
442        // get current time once again
443        timeAfterTick = timer_->getMicroseconds();
444
445        tickTime += timeAfterTick - timeBeforeTick;
446        if (timeAfterTick > refreshStartTime + refreshTime)
447        {
448          GraphicsEngine::getInstance().setAverageTickTime(
449              (float)tickTime * 0.001 / (frameCount - oldFrameCount));
450          float avgFPS = (float)(frameCount - oldFrameCount) / (timeAfterTick - refreshStartTime) * 1000000.0;
451          GraphicsEngine::getInstance().setAverageFramesPerSecond(avgFPS);
452
453          oldFrameCount = frameCount;
454          tickTime = 0;
455          refreshStartTime = timeAfterTick;
456        }
457
458        // don't forget to call _fireFrameStarted in ogre to make sure
459        // everything goes smoothly
460        Ogre::FrameEvent evt;
461        evt.timeSinceLastFrame = dt;
462        evt.timeSinceLastEvent = dt; // note: same time, but shouldn't matter anyway
463        ogreRoot._fireFrameStarted(evt);
464
465        if (Settings::showsGraphics())
466        {
467          // Pump messages in all registered RenderWindows
468          // This calls the WindowEventListener objects.
469          Ogre::WindowEventUtilities::messagePump();
470          // make sure the window stays active even when not focused
471          // (probably only necessary on windows)
472          GraphicsEngine::getInstance().setWindowActivity(true);
473
474          // tick CEGUI
475          GUIManager::getInstance().tick(dt);
476
477          // render
478          ogreRoot._updateAllRenderTargets();
479        }
480
481        // again, just to be sure ogre works fine
482        ogreRoot._fireFrameEnded(evt); // note: uses the same time as _fireFrameStarted
483
484        ++frameCount;
485    }
486    }
487    catch (std::exception& ex)
488    {
489      // something went wrong.
490      COUT(1) << ex.what() << std::endl;
491      COUT(1) << "Main loop was stopped by an unhandled exception. Shutting down." << std::endl;
492    }
493  }
494}
Note: See TracBrowser for help on using the repository browser.