Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/network/src/orxonox/Orxonox.cc @ 1739

Last change on this file since 1739 was 1735, checked in by scheusso, 16 years ago

network branch merged into trunk

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