Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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