Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 1136 was 1136, checked in by FelixSchulthess, 16 years ago

created InGameConsole

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