Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

merged console-branch back to trunk.
IMPORTANT: update your media directory!

you need TCL to compile. TCL is available here: http://www.tcl.tk/
another option is to check out https://svn.orxonox.net/ogre/tcl8.5.2/ and compile it by yourself. makefiles are in the 'macosx', 'unix' and 'win' subfolders.
FindTCL.cmake searches in the usual locations and in ../libs/tcl8.5.2/

the orxonox console can be activated with numpad-enter. whatever you enter will be parsed by TCL. if TCL doesn't know a command, it gets executed by orxonox.

simple tcl commands are: "puts text" to write "text" into the console, "expr 1+1" to calculate the result of the given expression. just try it by yourself with "puts [expr 1+1]".
[x] means: evaluate x and use the returnvalue as an argument. in this case the returned value is "2" and the resulting command therefore "puts 2".

you can combine orxonox and tcl commands. a simple orxonox command is "log text" that writes text into the console and the logfile. test it with "log [expr 1+1]" to write "2" into all output channels of orxonox. something more advanced: "log [clock seconds]" writes the seconds since 1970 into the logfile. feel free to combine both: "log [clock seconds]: 1+1 is [expr 1+1]"

TCL uses variables. to set a new variable, use "set varname value". you can use the variable wherever you want with $varname. with this we can make the above command a bit more elegant:
set myexpression 1+1
log [clock seconds]: $myexpression is [expr $myexpression]

read more about tcl in the wiki: http://wiki.tcl.tk/

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