Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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