Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/orxonox/Orxonox.cc @ 1752

Last change on this file since 1752 was 1752, checked in by landauf, 16 years ago
  • removed #include "core/CoreIncludes.h" from Gamestate.h and put it into the .cc files
  • removed a warning in Client.cc
  • Property svn:eol-style set to native
File size: 13.8 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 *   Co-authors:
25 *      Benjamin Knecht <beni_at_orxonox.net>, (C) 2007
26 *
27 */
28
29/**
30 @file
31 @brief Orxonox Main Class
32 */
33
34// Precompiled Headers
35#include "OrxonoxStableHeaders.h"
36#include "Orxonox.h"
37
38//****** STD *******
39#include <deque>
40
41//****** OGRE ******
42#include <OgreFrameListener.h>
43#include <OgreOverlay.h>
44#include <OgreOverlayManager.h>
45#include <OgreRoot.h>
46#include <OgreTimer.h>
47#include <OgreWindowEventUtilities.h>
48
49//***** ORXONOX ****
50// util
51//#include "util/Sleep.h"
52#include "util/ArgReader.h"
53
54// core
55#include "core/ConfigFileManager.h"
56#include "core/CoreIncludes.h"
57#include "core/Iterator.h"
58#include "core/ConsoleCommand.h"
59#include "core/Loader.h"
60#include "core/input/InputManager.h"
61#include "core/TclBind.h"
62#include "core/Core.h"
63#include "util/Debug.h"
64
65// audio
66#include "audio/AudioManager.h"
67
68// network
69#include "network/Server.h"
70#include "network/Client.h"
71
72// objects and tools
73#include "overlays/OverlayGroup.h"
74#include "overlays/console/InGameConsole.h"
75#include "objects/Tickable.h"
76#include "objects/Backlight.h"
77#include "tools/ParticleInterface.h"
78
79#include "GraphicsEngine.h"
80#include "Settings.h"
81#include "Radar.h"
82
83// globals for the server or client
84static network::Client *client_g = 0;
85static network::Server *server_g = 0;
86
87namespace orxonox
88{
89  SetConsoleCommandShortcut(Orxonox, exit).keybindMode(KeybindMode::OnPress);
90  SetConsoleCommandShortcut(Orxonox, slomo).accessLevel(AccessLevel::Offline).defaultValue(0, 1.0).axisParamIndex(0).isAxisRelative(false);
91  SetConsoleCommandShortcut(Orxonox, setTimeFactor).accessLevel(AccessLevel::Offline).defaultValue(0, 1.0);
92
93  /**
94    @brief Reference to the only instance of the class.
95  */
96  Orxonox *Orxonox::singletonRef_s = 0;
97
98  /**
99   * Create a new instance of Orxonox. Avoid doing any actual work here.
100   */
101  Orxonox::Orxonox()
102    : ogre_(0)
103    , startLevel_(0)
104    , hud_(0)
105    , radar_(0)
106    //, auMan_(0)
107    , timer_(0)
108    , bAbort_(false)
109    , timefactor_(1.0f)
110    , mode_(STANDALONE)
111    , serverIp_("")
112    , serverPort_(NETWORK_PORT)
113  {
114  }
115
116  /**
117   * Destruct Orxonox.
118   */
119  Orxonox::~Orxonox()
120  {
121    // keep in mind: the order of deletion is very important!
122    Loader::unload(startLevel_);
123    if (this->startLevel_)
124      delete this->startLevel_;
125
126    Loader::unload(hud_);
127    if (this->hud_)
128      delete this->hud_;
129
130    if (this->radar_)
131      delete this->radar_;
132
133    Loader::close();
134    //if (this->auMan_)
135    //  delete this->auMan_;
136    InGameConsole::getInstance().destroy();
137    if (this->timer_)
138      delete this->timer_;
139    InputManager::destroy();
140    GraphicsEngine::getSingleton().destroy();
141
142    if (client_g)
143      delete client_g;
144    if (server_g)
145      delete server_g;
146  }
147
148
149  /**
150    Asks the mainloop nicely to abort.
151  */
152  void Orxonox::abortRequest()
153  {
154    COUT(3) << "Orxonox: Abort requested." << std::endl;
155    bAbort_ = true;
156  }
157
158  /**
159   * @return singleton reference
160   */
161  Orxonox* Orxonox::getSingleton()
162  {
163    if (!singletonRef_s)
164      singletonRef_s = new Orxonox();
165    return singletonRef_s;
166  }
167
168  /**
169    @brief Destroys the Orxonox singleton.
170  */
171  void Orxonox::destroySingleton()
172  {
173    if (singletonRef_s)
174      delete singletonRef_s;
175    singletonRef_s = 0;
176  }
177
178  /**
179    @brief Changes the speed of Orxonox
180  */
181  void Orxonox::setTimeFactor(float factor)
182  {
183    float change = factor / Orxonox::getSingleton()->getTimeFactor();
184    Orxonox::getSingleton()->timefactor_ = factor;
185    for (ObjectList<ParticleInterface>::iterator it = ObjectList<ParticleInterface>::begin(); it; ++it)
186        it->setSpeedFactor(it->getSpeedFactor() * change);
187
188    for (ObjectList<Backlight>::iterator it = ObjectList<Backlight>::begin(); it; ++it)
189        it->setTimeFactor(Orxonox::getSingleton()->getTimeFactor());
190  }
191
192  /**
193   * initialization of Orxonox object
194   * @param argc argument counter
195   * @param argv list of argumenst
196   * @param path path to config (in home dir or something)
197   */
198  bool Orxonox::init(int argc, char **argv)
199  {
200#ifdef _DEBUG
201    ConfigFileManager::getInstance()->setFile(CFT_Settings, "orxonox_d.ini");
202#else
203    ConfigFileManager::getInstance()->setFile(CFT_Settings, "orxonox.ini");
204#endif
205    Factory::createClassHierarchy();
206
207    std::string mode;
208    std::string tempDataPath;
209
210    ArgReader ar(argc, argv);
211    ar.checkArgument("mode", &mode, false);
212    ar.checkArgument("data", &tempDataPath, false);
213    ar.checkArgument("ip",   &serverIp_, false);
214    ar.checkArgument("port", &serverPort_, false);
215    if(ar.errorHandling())
216    {
217      COUT(1) << "Error while parsing command line arguments" << std::endl;
218      COUT(1) << ar.getErrorString();
219      COUT(0) << "Usage:" << std::endl << "orxonox [mode client|server|dedicated|standalone] "
220        << "[--data PATH] [--ip IP] [--port PORT]" << std::endl;
221      return false;
222    }
223
224    if (mode == "client")
225      mode_ = CLIENT;
226    else if (mode == "server")
227      mode_ = SERVER;
228    else if (mode == "dedicated")
229      mode_ = DEDICATED;
230    else
231    {
232      if (mode == "")
233        mode = "standalone";
234      if (mode != "standalone")
235      {
236        COUT(2) << "Warning: mode \"" << mode << "\" doesn't exist. "
237          << "Defaulting to standalone" << std::endl;
238        mode = "standalone";
239      }
240      mode_ = STANDALONE;
241    }
242    COUT(3) << "Orxonox: Mode is " << mode << "." << std::endl;
243
244    if (tempDataPath != "")
245    {
246      if (tempDataPath[tempDataPath.size() - 1] != '/')
247        tempDataPath += "/";
248      Settings::tsetDataPath(tempDataPath);
249    }
250
251    // initialise TCL
252    TclBind::getInstance().setDataPath(Settings::getDataPath());
253
254    //if (mode_ == DEDICATED)
255      // TODO: decide what to do here
256    //else
257
258    // for playable server, client and standalone, the startup
259    // procedure until the GUI is identical
260
261    ogre_ = &GraphicsEngine::getSingleton();
262    if (!ogre_->setup())       // creates ogre root and other essentials
263      return false;
264
265    return true;
266  }
267
268  /**
269   * start modules
270   */
271  bool Orxonox::start()
272  {
273    if (mode_ == DEDICATED)
274    {
275      // do something else
276    }
277    else
278    { // not dedicated server
279      if (!ogre_->loadRenderer())    // creates the render window
280        return false;
281
282      // TODO: Spread this so that this call only initialises things needed for the Console
283      if (!ogre_->initialiseResources())
284        return false;
285
286      // Load the InGameConsole
287      InGameConsole::getInstance().initialise();
288
289      // Calls the InputManager which sets up the input devices.
290      // The render window width and height are used to set up the mouse movement.
291      if (!InputManager::initialise(ogre_->getWindowHandle(),
292            ogre_->getWindowWidth(), ogre_->getWindowHeight(), true, true, true))
293        return false;
294
295      // TOOD: load the GUI here
296      // set InputManager to GUI mode
297      InputManager::setInputState(InputManager::IS_GUI);
298      // TODO: run GUI here
299
300      // The following lines depend very much on the GUI output, so they're probably misplaced here..
301
302      InputManager::setInputState(InputManager::IS_NONE);
303
304      // create Ogre SceneManager
305      ogre_->createNewScene();
306
307      if (!loadPlayground())
308        return false;
309    }
310
311    switch (mode_)
312    {
313    case SERVER:
314      if (!serverLoad())
315        return false;
316      break;
317    case CLIENT:
318      if (!clientLoad())
319        return false;
320      break;
321    case DEDICATED:
322      if (!serverLoad())
323        return false;
324      break;
325    default:
326      if (!standaloneLoad())
327        return false;
328    }
329
330    InputManager::setInputState(InputManager::IS_NORMAL);
331
332    return startRenderLoop();
333  }
334
335  /**
336   * Loads everything in the scene except for the actual objects.
337   * This includes HUD, audio..
338   */
339  bool Orxonox::loadPlayground()
340  {
341    // Init audio
342    //auMan_ = new audio::AudioManager();
343    //auMan_->ambientAdd("a1");
344    //auMan_->ambientAdd("a2");
345    //auMan_->ambientAdd("a3");
346    //auMan->ambientAdd("ambient1");
347    //auMan_->ambientStart();
348
349    // Load the HUD
350    COUT(3) << "Orxonox: Loading HUD" << std::endl;
351    hud_ = new Level(Settings::getDataPath() + "overlay/hud.oxo");
352    Loader::load(hud_);
353
354    // Start the Radar
355    this->radar_ = new Radar();
356
357    return true;
358  }
359
360  /**
361   * Level loading method for server mode.
362   */
363  bool Orxonox::serverLoad()
364  {
365    COUT(0) << "Loading level in server mode" << std::endl;
366
367    server_g = new network::Server(serverPort_);
368    COUT(0) << "Loading scene in server mode" << std::endl;
369    if (!loadScene())
370      return false;
371
372    server_g->open();
373
374    return true;
375  }
376
377  /**
378   * Level loading method for client mode.
379   */
380  bool Orxonox::clientLoad()
381  {
382    COUT(0) << "Loading level in client mode" << std::endl;\
383
384    if (serverIp_.compare("") == 0)
385      client_g = new network::Client();
386    else
387
388      client_g = new network::Client(serverIp_, serverPort_);
389
390    if(!client_g->establishConnection())
391      return false;
392    client_g->tick(0);
393
394    return true;
395  }
396
397  /**
398   * Level loading method for standalone mode.
399   */
400  bool Orxonox::standaloneLoad()
401  {
402    COUT(0) << "Loading level in standalone mode" << std::endl;
403
404    if (!loadScene())
405      return false;
406
407    return true;
408  }
409
410  /**
411   * Helper method to load a level.
412   */
413  bool Orxonox::loadScene()
414  {
415    startLevel_ = new Level("levels/sample.oxw");
416    Loader::open(startLevel_);
417
418    return true;
419  }
420
421
422  /**
423    Main loop of the orxonox game.
424    About the loop: The design is almost exactly like the one in ogre, so that
425    if any part of ogre registers a framelisteners, it will still behave
426    correctly. Furthermore the time smoothing feature from ogre has been
427    implemented too. If turned on (see orxonox constructor), it will calculate
428    the dt_n by means of the recent most dt_n-1, dt_n-2, etc.
429  */
430  bool Orxonox::startRenderLoop()
431  {
432    // first check whether ogre root object has been created
433    if (Ogre::Root::getSingletonPtr() == 0)
434    {
435      COUT(2) << "Orxonox Error: Could not start rendering. No Ogre root object found" << std::endl;
436      return false;
437    }
438    Ogre::Root& ogreRoot = Ogre::Root::getSingleton();
439
440
441    // use the ogre timer class to measure time.
442    if (!timer_)
443      timer_ = new Ogre::Timer();
444
445    unsigned long frameCount = 0;
446
447    // TODO: this would very well fit into a configValue
448    const unsigned long refreshTime = 200000;
449    unsigned long refreshStartTime = 0;
450    unsigned long tickTime = 0;
451    unsigned long oldFrameCount = 0;
452
453    unsigned long timeBeforeTick = 0;
454    unsigned long timeBeforeTickOld = 0;
455    unsigned long timeAfterTick = 0;
456
457    COUT(3) << "Orxonox: Starting the main loop." << std::endl;
458
459    timer_->reset();
460    while (!bAbort_)
461    {
462      // get current time
463      timeBeforeTickOld = timeBeforeTick;
464      timeBeforeTick    = timer_->getMicroseconds();
465      float dt = (timeBeforeTick - timeBeforeTickOld) / 1000000.0;
466
467
468      // tick the core (needs real time for input and tcl thread management)
469      Core::tick(dt);
470
471      // Call those objects that need the real time
472      for (ObjectList<TickableReal>::iterator it = ObjectList<TickableReal>::begin(); it; ++it)
473        it->tick(dt);
474      // Call the scene objects
475      for (ObjectList<Tickable>::iterator it = ObjectList<Tickable>::begin(); it; ++it)
476        it->tick(dt * this->timefactor_);
477
478      // call server/client with normal dt
479      if (client_g)
480        client_g->tick(dt * this->timefactor_);
481      if (server_g)
482        server_g->tick(dt * this->timefactor_);
483
484
485      // get current time once again
486      timeAfterTick = timer_->getMicroseconds();
487
488      tickTime += timeAfterTick - timeBeforeTick;
489      if (timeAfterTick > refreshStartTime + refreshTime)
490      {
491        GraphicsEngine::getSingleton().setAverageTickTime(
492            (float)tickTime * 0.001 / (frameCount - oldFrameCount));
493        GraphicsEngine::getSingleton().setAverageFramesPerSecond(
494            (float)(frameCount - oldFrameCount) / (timeAfterTick - refreshStartTime) * 1000000.0);
495        oldFrameCount = frameCount;
496        tickTime = 0;
497        refreshStartTime = timeAfterTick;
498      }
499
500
501      // don't forget to call _fireFrameStarted in ogre to make sure
502      // everything goes smoothly
503      Ogre::FrameEvent evt;
504      evt.timeSinceLastFrame = dt;
505      evt.timeSinceLastEvent = dt; // note: same time, but shouldn't matter anyway
506      ogreRoot._fireFrameStarted(evt);
507
508      if (mode_ != DEDICATED)
509      {
510        // Pump messages in all registered RenderWindows
511        // This calls the WindowEventListener objects.
512        Ogre::WindowEventUtilities::messagePump();
513        // make sure the window stays active even when not focused
514        // (probably only necessary on windows)
515        GraphicsEngine::getSingleton().setWindowActivity(true);
516
517        // render
518        ogreRoot._updateAllRenderTargets();
519      }
520
521      // again, just to be sure ogre works fine
522      ogreRoot._fireFrameEnded(evt); // note: uses the same time as _fireFrameStarted
523
524      ++frameCount;
525    }
526
527    if (mode_ == CLIENT)
528      client_g->closeConnection();
529    else if (mode_ == SERVER)
530      server_g->close();
531
532    return true;
533  }
534}
Note: See TracBrowser for help on using the repository browser.