Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/console/src/orxonox/Orxonox.cc @ 1513

Last change on this file since 1513 was 1341, checked in by landauf, 17 years ago

big commit, but not much changes in code:

  • put CommandEvaluation into it's own .cc and .h files
  • put some basic ConsoleCommands into ConsoleCommandCompilation.cc and .h
  • created a new class, ConsoleCommand, inheriting from ExecutorStatic, implementing all command-related features that were located in the Executor until now (at the moment only accessLevel_, but more will come - from reto and me)
  • renamed ConsoleCommand-macros to SetConsoleCommand (all related macros were changed the same way)
  • added a new command named "killdelays", that kills all delayed commands. helpful to stop disco ;)
File size: 12.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 *      Benjamin Knecht <beni_at_orxonox.net>, (C) 2007
24 *   Co-authors:
25 *      ...
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 <iostream>
40//#include <exception>
41#include <deque>
42
43//****** OGRE ******
44//#include <OgreException.h>
45#include <OgreFrameListener.h>
46#include <OgreOverlay.h>
47#include <OgreOverlayManager.h>
48#include <OgreRoot.h>
49#include <OgreTimer.h>
50#include <OgreWindowEventUtilities.h>
51
52//***** ORXONOX ****
53// util
54//#include "util/Sleep.h"
55#include "util/ArgReader.h"
56
57// core
58#include "core/ConfigFileManager.h"
59#include "core/ConsoleCommand.h"
60#include "core/Debug.h"
61#include "core/Loader.h"
62#include "core/Tickable.h"
63#include "core/InputManager.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 "hud/HUD.h"
74//#include "console/InGameConsole.h"
75
76// FIXME: is this really file scope?
77// globals for the server or client
78network::Client *client_g;
79network::Server *server_g;
80
81namespace orxonox
82{
83  SetConsoleCommand(Orxonox, exit, true);
84  SetConsoleCommand(Orxonox, slomo, true).setDefaultValue(0, 1.0).setAccessLevel(AccessLevel::Offline);
85  SetConsoleCommand(Orxonox, setTimeFactor, false).setDefaultValue(0, 1.0).setAccessLevel(AccessLevel::Offline);
86
87  /**
88    @brief Reference to the only instance of the class.
89  */
90  Orxonox *Orxonox::singletonRef_s = 0;
91
92  /**
93   * create a new instance of Orxonox
94   */
95  Orxonox::Orxonox()
96  {
97    this->ogre_ = &GraphicsEngine::getSingleton();
98    this->timer_ = 0;
99    this->dataPath_ = "";
100    this->auMan_ = 0;
101    this->inputHandler_ = 0;
102    // turn on frame smoothing by setting a value different from 0
103    this->frameSmoothingTime_ = 0.0f;
104    this->bAbort_ = false;
105    this->timefactor_ = 1.0f;
106  }
107
108  /**
109   * destruct Orxonox
110   */
111  Orxonox::~Orxonox()
112  {
113    // keep in mind: the order of deletion is very important!
114    if (this->orxonoxHUD_)
115      delete this->orxonoxHUD_;
116    Loader::close();
117    InputManager::getSingleton().destroy();
118    if (this->auMan_)
119      delete this->auMan_;
120    if (this->timer_)
121      delete this->timer_;
122    GraphicsEngine::getSingleton().destroy();
123
124    if (client_g)
125      delete client_g;
126    if (server_g)
127      delete server_g;
128  }
129
130  /**
131    @brief Immediately deletes the orxonox object.
132    Never use if you can help it while rendering!
133  */
134  void Orxonox::abortImmediateForce()
135  {
136    COUT(1) << "*** Orxonox Error: Orxonox object was unexpectedly destroyed." << std::endl;
137    delete this;
138  }
139
140  /**
141    Asks the mainloop nicely to abort.
142  */
143  void Orxonox::abortRequest()
144  {
145    COUT(3) << "*** Orxonox: Abort requested." << std::endl;
146    bAbort_ = true;
147  }
148
149  /**
150   * @return singleton object
151   */
152  Orxonox* Orxonox::getSingleton()
153  {
154    if (!singletonRef_s)
155      singletonRef_s = new Orxonox();
156    return singletonRef_s;
157  }
158
159  /**
160    @brief Destroys the Orxonox singleton.
161  */
162  void Orxonox::destroySingleton()
163  {
164    if (singletonRef_s)
165      delete singletonRef_s;
166    singletonRef_s = 0;
167  }
168
169  /**
170   * initialization of Orxonox object
171   * @param argc argument counter
172   * @param argv list of argumenst
173   * @param path path to config (in home dir or something)
174   */
175  void Orxonox::init(int argc, char **argv, std::string path)
176  {
177    //TODO: find config file (assuming executable directory)
178    //TODO: read config file
179    //TODO: give config file to Ogre
180    std::string mode;
181
182    ArgReader ar(argc, argv);
183    ar.checkArgument("mode", mode, false);
184    ar.checkArgument("data", this->dataPath_, false);
185    ar.checkArgument("ip", serverIp_, false);
186    if(ar.errorHandling()) abortImmediateForce();
187    if(mode == std::string("client"))
188    {
189      mode_ = CLIENT;
190      clientInit(path);
191    }
192    else if(mode== std::string("server")){
193      mode_ = SERVER;
194      serverInit(path);
195    }
196    else{
197      mode_ = STANDALONE;
198      standaloneInit(path);
199    }
200  }
201
202  void Orxonox::serverInit(std::string path)
203  {
204    COUT(2) << "initialising server" << std::endl;
205
206    ogre_->setConfigPath(path);
207    ogre_->setup();
208    //root_ = ogre_->getRoot();
209    if(!ogre_->load(this->dataPath_)) abortImmediateForce(/* unable to load */);
210
211    server_g = new network::Server();
212  }
213
214  void Orxonox::clientInit(std::string path)
215  {
216    COUT(2) << "initialising client" << std::endl;\
217
218    ogre_->setConfigPath(path);
219    ogre_->setup();
220    if(serverIp_.compare("")==0)
221      client_g = new network::Client();
222    else
223      client_g = new network::Client(serverIp_, NETWORK_PORT);
224    if(!ogre_->load(this->dataPath_)) abortImmediateForce(/* unable to load */);
225  }
226
227  void Orxonox::standaloneInit(std::string path)
228  {
229    COUT(2) << "initialising standalone mode" << std::endl;
230
231    ogre_->setConfigPath(path);
232    ogre_->setup();
233    if(!ogre_->load(this->dataPath_)) abortImmediateForce(/* unable to load */);
234  }
235
236  /**
237   * start modules
238   */
239  void Orxonox::start()
240  {
241    switch(mode_){
242    case CLIENT:
243      clientStart();
244      break;
245    case SERVER:
246      serverStart();
247      break;
248    default:
249      standaloneStart();
250    }
251  }
252
253  void Orxonox::clientStart(){
254    ogre_->initialise();
255    ConfigFileManager::getSingleton()->setFile(CFT_Settings, "orxonox.ini");
256    Factory::createClassHierarchy();
257
258
259    auMan_ = new audio::AudioManager();
260
261    Ogre::Overlay* hudOverlay = Ogre::OverlayManager::getSingleton().getByName("Orxonox/HUD1.2");
262    HUD* orxonoxHud;
263    orxonoxHud = new HUD();
264    orxonoxHud->setEnergyValue(20);
265    orxonoxHud->setEnergyDistr(20,20,60);
266    hudOverlay->show();
267
268    client_g->establishConnection();
269    client_g->tick(0);
270
271
272    //setupInputSystem();
273
274    startRenderLoop();
275  }
276
277  void Orxonox::serverStart(){
278    //TODO: start modules
279    ogre_->initialise();
280    //TODO: run engine
281    ConfigFileManager::getSingleton()->setFile(CFT_Settings, "orxonox.ini");
282    Factory::createClassHierarchy();
283    createScene();
284    setupInputSystem();
285
286    server_g->open();
287
288    startRenderLoop();
289  }
290
291  void Orxonox::standaloneStart(){
292    //TODO: start modules
293    ogre_->initialise();
294    //TODO: run engine
295    ConfigFileManager::getSingleton()->setFile(CFT_Settings, "orxonox.ini");
296    Factory::createClassHierarchy();
297    createScene();
298    setupInputSystem();
299
300    startRenderLoop();
301  }
302
303  void Orxonox::createScene(void)
304  {
305          // Init audio
306    auMan_ = new audio::AudioManager();
307
308    // load this file from config
309    Level* startlevel = new Level("levels/sample.oxw");
310    Loader::open(startlevel);
311
312    Ogre::Overlay* hudOverlay = Ogre::OverlayManager::getSingleton().getByName("Orxonox/HUD1.2");
313    orxonoxHUD_ = new HUD();
314    orxonoxHUD_->setEnergyValue(20);
315    orxonoxHUD_->setEnergyDistr(20,20,60);
316    hudOverlay->show();
317
318        /*
319    auMan_->ambientAdd("a1");
320    auMan_->ambientAdd("a2");
321    auMan_->ambientAdd("a3");
322    //auMan->ambientAdd("ambient1");
323    auMan_->ambientStart();
324  */
325  }
326
327  /**
328    @brief Calls the InputHandler which sets up the input devices.
329    The render window width and height are used to set up the mouse movement.
330  */
331  void Orxonox::setupInputSystem()
332  {
333    inputHandler_ = &InputManager::getSingleton();
334    if (!inputHandler_->initialise(ogre_->getWindowHandle(),
335          ogre_->getWindowWidth(), ogre_->getWindowHeight()))
336      abortImmediateForce();
337    inputHandler_->setInputMode(IM_INGAME);
338  }
339
340  /**
341    Main loop of the orxonox game.
342    This is a new solution, using the ogre engine instead of being used by it.
343    An alternative solution would be to simply use the timer of the Root object,
344    but that implies using Ogre in any case. There would be no way to test
345    our code without the help of the root object.
346    There's even a chance that we can dispose of the root object entirely
347    in server mode.
348    About the loop: The design is almost exactly like the one in ogre, so that
349    if any part of ogre registers a framelisteners, it will still behave
350    correctly. Furthermore the time smoothing feature from ogre has been
351    implemented too. If turned on (see orxonox constructor), it will calculate
352    the dt_n by means of the recent most dt_n-1, dt_n-2, etc.
353  */
354  void Orxonox::startRenderLoop()
355  {
356    // first check whether ogre root object has been created
357    if (Ogre::Root::getSingletonPtr() == 0)
358    {
359      COUT(2) << "*** Orxonox Error: Could not start rendering. No Ogre root object found" << std::endl;
360      return;
361    }
362    Ogre::Root& ogreRoot = Ogre::Root::getSingleton();
363
364
365    // Contains the times of recently fired events
366    // eventTimes[4] is the list for the times required for the fps counter
367    std::deque<unsigned long> eventTimes[4];
368    // Clear event times
369    for (int i = 0; i < 4; ++i)
370      eventTimes[i].clear();
371    // fill the fps time list with zeros
372    for (int i = 0; i < 20; i++)
373      eventTimes[3].push_back(0);
374
375    // use the ogre timer class to measure time.
376    if (!timer_)
377      timer_ = new Ogre::Timer();
378    timer_->reset();
379
380          while (!bAbort_)
381          {
382                  // Pump messages in all registered RenderWindows
383      // This calls the WindowEventListener objects.
384      Ogre::WindowEventUtilities::messagePump();
385
386      // get current time
387      unsigned long now = timer_->getMilliseconds();
388      eventTimes[3].push_back(now);
389      eventTimes[3].erase(eventTimes[3].begin());
390
391      // create an event to pass to the frameStarted method in ogre
392      Ogre::FrameEvent evt;
393      evt.timeSinceLastEvent = calculateEventTime(now, eventTimes[0]);
394      evt.timeSinceLastFrame = calculateEventTime(now, eventTimes[1]);
395
396      // show the current time in the HUD
397      orxonoxHUD_->setTime((int)now, 0);
398      if (eventTimes[3].back() - eventTimes[3].front() != 0)
399        orxonoxHUD_->setRocket1((int)(20000.0f/(eventTimes[3].back() - eventTimes[3].front())));
400
401      // Iterate through all Tickables and call their tick(dt) function
402      for (Iterator<Tickable> it = ObjectList<Tickable>::start(); it; ++it)
403        it->tick((float)evt.timeSinceLastFrame * this->timefactor_);
404
405      // don't forget to call _fireFrameStarted in ogre to make sure
406      // everything goes smoothly
407      ogreRoot._fireFrameStarted(evt);
408
409      // server still renders at the moment
410      //if (mode_ != SERVER)
411      ogreRoot._updateAllRenderTargets(); // only render in non-server mode
412
413      // get current time
414      now = timer_->getMilliseconds();
415
416      // create an event to pass to the frameEnded method in ogre
417      evt.timeSinceLastEvent = calculateEventTime(now, eventTimes[0]);
418      evt.timeSinceLastFrame = calculateEventTime(now, eventTimes[2]);
419
420      // again, just to be sure ogre works fine
421      ogreRoot._fireFrameEnded(evt);
422          }
423  }
424
425  /**
426    Method for calculating the average time between recently fired events.
427    Code directly taken from OgreRoot.cc
428    @param now The current time in ms.
429    @param type The type of event to be considered.
430  */
431  float Orxonox::calculateEventTime(unsigned long now, std::deque<unsigned long> &times)
432  {
433    // Calculate the average time passed between events of the given type
434    // during the last frameSmoothingTime_ seconds.
435
436    times.push_back(now);
437
438    if(times.size() == 1)
439      return 0;
440
441    // Times up to frameSmoothingTime_ seconds old should be kept
442    unsigned long discardThreshold = (unsigned long)(frameSmoothingTime_ * 1000.0f);
443
444    // Find the oldest time to keep
445    std::deque<unsigned long>::iterator it  = times.begin();
446    // We need at least two times
447    std::deque<unsigned long>::iterator end = times.end() - 2;
448
449    while(it != end)
450    {
451      if (now - *it > discardThreshold)
452        ++it;
453      else
454        break;
455    }
456
457    // Remove old times
458    times.erase(times.begin(), it);
459
460    return (float)(times.back() - times.front()) / ((times.size() - 1) * 1000);
461  }
462
463  /**
464    @brief Test method for the InputHandler.
465    But: Is actually responsible for catching an exit event..
466  */
467  void Orxonox::eventOccured(InputEvent &evt)
468  {
469    if (evt.id == 1)
470      this->abortRequest();
471  }
472}
Note: See TracBrowser for help on using the repository browser.