Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/network2/src/orxonox/Orxonox.cc @ 1098

Last change on this file since 1098 was 1098, checked in by rgrieder, 16 years ago

merged network branch into new network2 branch (from trunk)

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