Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/network/src/orxonox/Orxonox.cc @ 1450

Last change on this file since 1450 was 1446, checked in by landauf, 16 years ago

merged console branch into network branch

after several heavy troubles it compiles, but there is still a bug I couldn't fix: orxonox crashes as soon as one presses a key after opening the console… maybe someone else sees the problem?

File size: 12.4 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 <deque>
40
41//****** OGRE ******
42#include <OgreFrameListener.h>
43#include <OgreOverlay.h>
44#include <OgreOverlayManager.h>
45#include <OgreRoot.h>
46#include <OgreTimer.h>
47#include <OgreWindowEventUtilities.h>
48
49//***** ORXONOX ****
50// util
51//#include "util/Sleep.h"
52#include "util/ArgReader.h"
53
54// core
55#include "core/ConfigFileManager.h"
56#include "core/ConsoleCommand.h"
57#include "core/Debug.h"
58#include "core/Loader.h"
59#include "core/Tickable.h"
60#include "core/InputManager.h"
61#include "core/TclBind.h"
62
63// audio
64#include "audio/AudioManager.h"
65
66// network
67#include "network/Server.h"
68#include "network/Client.h"
69
70// objects and tools
71#include "hud/HUD.h"
72
73// FIXME: is this really file scope?
74// globals for the server or client
75network::Client *client_g;
76network::Server *server_g;
77
78namespace orxonox
79{
80  SetConsoleCommandShortcut(Orxonox, exit).setKeybindMode(KeybindMode::OnPress);
81  SetConsoleCommandShortcut(Orxonox, slomo).setAccessLevel(AccessLevel::Offline).setDefaultValue(0, 1.0).setAxisParamIndex(0).setIsAxisRelative(false);
82  SetConsoleCommandShortcut(Orxonox, setTimeFactor).setAccessLevel(AccessLevel::Offline).setDefaultValue(0, 1.0);
83
84  /**
85    @brief Reference to the only instance of the class.
86  */
87  Orxonox *Orxonox::singletonRef_s = 0;
88
89  /**
90   * Create a new instance of Orxonox. Avoid doing any actual work here.
91   */
92  Orxonox::Orxonox() :
93    ogre_(0),
94    //auMan_(0),
95    timer_(0),
96    // turn on frame smoothing by setting a value different from 0
97    frameSmoothingTime_(0.0f),
98    orxonoxHUD_(0),
99    bAbort_(false),
100    timefactor_(1.0f),
101    mode_(STANDALONE),
102    serverIp_(""),
103    serverPort_(NETWORK_PORT)
104  {
105  }
106
107  /**
108   * Destruct Orxonox.
109   */
110  Orxonox::~Orxonox()
111  {
112    // keep in mind: the order of deletion is very important!
113//    if (this->orxonoxHUD_)
114//      delete this->orxonoxHUD_;
115    Loader::close();
116    InputManager::destroy();
117    //if (this->auMan_)
118    //  delete this->auMan_;
119    if (this->timer_)
120      delete this->timer_;
121    GraphicsEngine::getSingleton().destroy();
122
123    if (network::Client::getSingleton())
124      network::Client::destroySingleton();
125    if (server_g)
126      delete server_g;
127  }
128
129
130  /**
131    Asks the mainloop nicely to abort.
132  */
133  void Orxonox::abortRequest()
134  {
135    COUT(3) << "Orxonox: Abort requested." << std::endl;
136    bAbort_ = true;
137  }
138
139  /**
140   * @return singleton reference
141   */
142  Orxonox* Orxonox::getSingleton()
143  {
144    if (!singletonRef_s)
145      singletonRef_s = new Orxonox();
146    return singletonRef_s;
147  }
148
149  /**
150    @brief Destroys the Orxonox singleton.
151  */
152  void Orxonox::destroySingleton()
153  {
154    if (singletonRef_s)
155      delete singletonRef_s;
156    singletonRef_s = 0;
157  }
158
159  /**
160   * initialization of Orxonox object
161   * @param argc argument counter
162   * @param argv list of argumenst
163   * @param path path to config (in home dir or something)
164   */
165  bool Orxonox::init(int argc, char **argv, std::string path)
166  {
167    //TODO: find config file (assuming executable directory)
168    //TODO: read config file
169    //TODO: give config file to Ogre
170    std::string mode;
171    std::string dataPath;
172
173    ArgReader ar(argc, argv);
174    ar.checkArgument("mode", mode, false);
175    ar.checkArgument("data", dataPath, false);
176    ar.checkArgument("ip", serverIp_, false);
177    ar.checkArgument("port", serverPort_, false);
178    if(ar.errorHandling())
179      return false;
180
181    if (mode == "client")
182      mode_ = CLIENT;
183    else if (mode == "server")
184      mode_ = SERVER;
185    else
186    {
187      mode = "standalone";
188      mode_ = STANDALONE;
189    }
190    COUT(3) << "Orxonox: Mode is " << mode << "." << std::endl;
191
192    //if (mode_ == DEDICATED)
193      // TODO: decide what to do here
194    //else
195
196    // for playable server, client and standalone, the startup
197    // procedure until the GUI is identical
198
199    TclBind::getInstance().setDataPath(dataPath);
200    ConfigFileManager::getSingleton()->setFile(CFT_Settings, "orxonox.ini");
201    Factory::createClassHierarchy();
202
203    ogre_ = &GraphicsEngine::getSingleton();
204    if (!ogre_->setup(path))       // creates ogre root and other essentials
205      return false;
206
207    return true;
208  }
209
210  /**
211   * start modules
212   */
213  bool Orxonox::start()
214  {
215    //if (mode == DEDICATED)
216    // do something else
217    //else
218
219    if (!ogre_->loadRenderer())    // creates the render window
220      return false;
221
222    // Calls the InputManager which sets up the input devices.
223    // The render window width and height are used to set up the mouse movement.
224    if (!InputManager::initialise(ogre_->getWindowHandle(),
225          ogre_->getWindowWidth(), ogre_->getWindowHeight(), true, true, true))
226      return false;
227
228    // TODO: Spread this so that this call only initialises things needed for the GUI
229    if (!ogre_->initialiseResources())
230      return false;
231
232    // TOOD: load the GUI here
233    // set InputManager to GUI mode
234    InputManager::setInputState(InputManager::IS_GUI);
235    // TODO: run GUI here
236
237    // The following lines depend very much on the GUI output, so they're probably misplaced here..
238
239    InputManager::setInputState(InputManager::IS_NONE);
240
241    if (!loadPlayground())
242      return false;
243
244    switch (mode_)
245    {
246    case SERVER:
247      if (!serverLoad())
248        return false;
249      break;
250    case CLIENT:
251      if (!clientLoad())
252        return false;
253      break;
254    default:
255      if (!standaloneLoad())
256        return false;
257    }
258
259    InputManager::setInputState(InputManager::IS_NORMAL);
260
261    return startRenderLoop();
262  }
263
264  /**
265   * Loads everything in the scene except for the actual objects.
266   * This includes HUD, Console..
267   */
268  bool Orxonox::loadPlayground()
269  {
270    ogre_->createNewScene();
271
272          // Init audio
273    //auMan_ = new audio::AudioManager();
274    //auMan_->ambientAdd("a1");
275    //auMan_->ambientAdd("a2");
276    //auMan_->ambientAdd("a3");
277    //auMan->ambientAdd("ambient1");
278    //auMan_->ambientStart();
279
280    // Load the HUD
281    COUT(3) << "Orxonox: Loading HUD..." << std::endl;
282    orxonoxHUD_ = &HUD::getSingleton();
283    return true;
284  }
285
286  /**
287   * Level loading method for server mode.
288   */
289  bool Orxonox::serverLoad()
290  {
291    COUT(2) << "Loading level in server mode" << std::endl;
292
293    server_g = new network::Server(serverPort_);
294
295    if (!loadScene())
296      return false;
297
298    server_g->open();
299
300    return true;
301  }
302
303  /**
304   * Level loading method for client mode.
305   */
306  bool Orxonox::clientLoad()
307  {
308    COUT(2) << "Loading level in client mode" << std::endl;\
309
310    if (serverIp_.compare("") == 0)
311      client_g = network::Client::createSingleton();
312    else
313
314      client_g = network::Client::createSingleton(serverIp_, serverPort_);
315
316    if(!client_g->establishConnection())
317      return false;
318    client_g->tick(0);
319
320    return true;
321  }
322
323  /**
324   * Level loading method for standalone mode.
325   */
326  bool Orxonox::standaloneLoad()
327  {
328    COUT(2) << "Loading level in standalone mode" << std::endl;
329
330    if (!loadScene())
331      return false;
332
333    return true;
334  }
335
336  /**
337   * Helper method to load a level.
338   */
339  bool Orxonox::loadScene()
340  {
341    Level* startlevel = new Level("levels/sample.oxw");
342    Loader::open(startlevel);
343
344    return true;
345  }
346
347
348  /**
349    Main loop of the orxonox game.
350    About the loop: The design is almost exactly like the one in ogre, so that
351    if any part of ogre registers a framelisteners, it will still behave
352    correctly. Furthermore the time smoothing feature from ogre has been
353    implemented too. If turned on (see orxonox constructor), it will calculate
354    the dt_n by means of the recent most dt_n-1, dt_n-2, etc.
355  */
356  bool Orxonox::startRenderLoop()
357  {
358    // first check whether ogre root object has been created
359    if (Ogre::Root::getSingletonPtr() == 0)
360    {
361      COUT(2) << "Orxonox Error: Could not start rendering. No Ogre root object found" << std::endl;
362      return false;
363    }
364    Ogre::Root& ogreRoot = Ogre::Root::getSingleton();
365
366
367    // Contains the times of recently fired events
368    // eventTimes[4] is the list for the times required for the fps counter
369    std::deque<unsigned long> eventTimes[3];
370    // Clear event times
371    for (int i = 0; i < 3; ++i)
372      eventTimes[i].clear();
373
374    // use the ogre timer class to measure time.
375    if (!timer_)
376      timer_ = new Ogre::Timer();
377    timer_->reset();
378
379    float renderTime = 0.0f;
380    float frameTime = 0.0f;
381    clock_t time = 0;
382
383    COUT(3) << "Orxonox: Starting the main loop." << std::endl;
384          while (!bAbort_)
385          {
386                  // Pump messages in all registered RenderWindows
387      // This calls the WindowEventListener objects.
388      Ogre::WindowEventUtilities::messagePump();
389
390      // get current time
391      unsigned long now = timer_->getMilliseconds();
392
393      // create an event to pass to the frameStarted method in ogre
394      Ogre::FrameEvent evt;
395      evt.timeSinceLastEvent = calculateEventTime(now, eventTimes[0]);
396      evt.timeSinceLastFrame = calculateEventTime(now, eventTimes[1]);
397      frameTime += evt.timeSinceLastFrame;
398
399      // show the current time in the HUD
400      // HUD::getSingleton().setTime(now);
401      if (frameTime > 0.4f)
402      {
403        HUD::getSingleton().setRenderTimeRatio(renderTime / frameTime);
404        frameTime = 0.0f;
405        renderTime = 0.0f;
406      }
407
408      // Call those objects that need the real time
409      for (Iterator<TickableReal> it = ObjectList<TickableReal>::start(); it; ++it)
410        it->tick((float)evt.timeSinceLastFrame);
411      // Call the scene objects
412      for (Iterator<Tickable> it = ObjectList<Tickable>::start(); it; ++it)
413        it->tick((float)evt.timeSinceLastFrame * this->timefactor_);
414
415      // don't forget to call _fireFrameStarted in ogre to make sure
416      // everything goes smoothly
417      ogreRoot._fireFrameStarted(evt);
418
419      // get current time
420      now = timer_->getMilliseconds();
421      calculateEventTime(now, eventTimes[2]);
422
423      ogreRoot._updateAllRenderTargets(); // only render in non-server mode
424
425      // get current time
426      now = timer_->getMilliseconds();
427
428      // create an event to pass to the frameEnded method in ogre
429      evt.timeSinceLastEvent = calculateEventTime(now, eventTimes[0]);
430      renderTime += calculateEventTime(now, eventTimes[2]);
431
432      // again, just to be sure ogre works fine
433      ogreRoot._fireFrameEnded(evt);
434          }
435
436    if (mode_==CLIENT)
437      network::Client::getSingleton()->closeConnection();
438    else if (mode_==SERVER)
439      server_g->close();
440
441    return true;
442  }
443
444  /**
445    Method for calculating the average time between recently fired events.
446    Code directly taken from OgreRoot.cc
447    @param now The current time in ms.
448    @param type The type of event to be considered.
449  */
450  float Orxonox::calculateEventTime(unsigned long now, std::deque<unsigned long> &times)
451  {
452    // Calculate the average time passed between events of the given type
453    // during the last frameSmoothingTime_ seconds.
454
455    times.push_back(now);
456
457    if(times.size() == 1)
458      return 0;
459
460    // Times up to frameSmoothingTime_ seconds old should be kept
461    unsigned long discardThreshold = (unsigned long)(frameSmoothingTime_ * 1000.0f);
462
463    // Find the oldest time to keep
464    std::deque<unsigned long>::iterator it  = times.begin();
465    // We need at least two times
466    std::deque<unsigned long>::iterator end = times.end() - 2;
467
468    while(it != end)
469    {
470      if (now - *it > discardThreshold)
471        ++it;
472      else
473        break;
474    }
475
476    // Remove old times
477    times.erase(times.begin(), it);
478
479    return (float)(times.back() - times.front()) / ((times.size() - 1) * 1000);
480  }
481}
Note: See TracBrowser for help on using the repository browser.