Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/netp6/src/core/Game.cc @ 3593

Last change on this file since 3593 was 3299, checked in by rgrieder, 15 years ago

Sometimes I honestly ask myself why such things could run on Linux, magic I suppose

  • Property svn:eol-style set to native
File size: 14.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 *      Reto Grieder
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29/**
30@file
31@brief
32    Implementation of the Game class.
33*/
34
35#include "Game.h"
36
37#include <exception>
38#include <boost/weak_ptr.hpp>
39
40#include "util/Debug.h"
41#include "util/Exception.h"
42#include "util/Sleep.h"
43#include "util/SubString.h"
44#include "Clock.h"
45#include "CommandLine.h"
46#include "ConsoleCommand.h"
47#include "Core.h"
48#include "CoreIncludes.h"
49#include "ConfigValueIncludes.h"
50#include "GameState.h"
51
52namespace orxonox
53{
54    using boost::shared_ptr;
55    using boost::weak_ptr;
56
57    static void stop_game()
58        { Game::getInstance().stop(); }
59    SetConsoleCommandShortcutExternAlias(stop_game, "exit");
60
61    struct _CoreExport GameStateTreeNode
62    {
63        GameState* state_;
64        weak_ptr<GameStateTreeNode> parent_;
65        std::vector<shared_ptr<GameStateTreeNode> > children_;
66    };
67
68    std::map<std::string, GameState*> Game::allStates_s;
69    Game* Game::singletonRef_s = 0;
70
71    /**
72    @brief
73        Non-initialising constructor.
74    */
75    Game::Game(int argc, char** argv)
76    {
77        assert(singletonRef_s == 0);
78        singletonRef_s = this;
79
80        this->abort_ = false;
81
82        // reset statistics
83        this->statisticsStartTime_ = 0;
84        this->statisticsTickTimes_.clear();
85        this->periodTickTime_ = 0;
86        this->periodTime_ = 0;
87        this->avgFPS_ = 0.0f;
88        this->avgTickTime_ = 0.0f;
89
90
91        // Set up a basic clock to keep time
92        this->gameClock_ = new Clock();
93
94        this->core_ = new orxonox::Core();
95        this->core_->initialise(argc, argv);
96
97        RegisterRootObject(Game);
98        this->setConfigValues();
99    }
100
101    /**
102    @brief
103    */
104    Game::~Game()
105    {
106        // Destroy pretty much everyhting left
107        delete this->core_;
108
109        delete this->gameClock_;
110
111        assert(singletonRef_s);
112        singletonRef_s = 0;
113    }
114
115    void Game::setConfigValues()
116    {
117        SetConfigValue(statisticsRefreshCycle_, 250000)
118            .description("Sets the time in microseconds interval at which average fps, etc. get updated.");
119        SetConfigValue(statisticsAvgLength_, 1000000)
120            .description("Sets the time in microseconds interval at which average fps, etc. gets calculated.");
121        SetConfigValue(levelName_, "presentation_dm.oxw")
122            .description("Sets the preselection of the level in the main menu.");
123      SetConfigValue(FPSLimit_, 50)
124            .description("Sets the desired framerate (0 for no limit).");
125    }
126
127    void Game::setLevel(std::string levelName)
128    {
129        ModifyConfigValue(levelName_, set, levelName);
130    }
131
132    std::string Game::getLevel()
133    {
134        std::string levelName;
135        CommandLine::getValue("level", &levelName);
136        if (levelName == "")
137            return levelName_;
138        else
139            return levelName;
140    }
141
142    /**
143    @brief
144        Main loop of the orxonox game.
145    @note
146        We use the Ogre::Timer to measure time since it uses the most precise
147        method an any platform (however the windows timer lacks time when under
148        heavy kernel load!).
149    */
150    void Game::run()
151    {
152        // Always start with the ROOT state
153        this->requestedStateNodes_.push_back(this->rootStateNode_);
154        this->activeStateNode_ = this->rootStateNode_;
155        this->loadState(this->rootStateNode_->state_);
156
157        // START GAME
158        this->gameClock_->capture(); // first delta time should be about 0 seconds
159        // A first item is required for the fps limiter
160        statisticsTickInfo tickInfo = {0, 0};
161        statisticsTickTimes_.push_back(tickInfo);
162        while (!this->abort_ && !this->activeStates_.empty())
163        {
164            uint64_t currentTime = this->gameClock_->getRealMicroseconds();
165
166            uint64_t nextTickTime = statisticsTickTimes_.back().tickTime + 1000000.f/this->FPSLimit_;
167            if( currentTime < nextTickTime )
168            {
169                usleep( nextTickTime - currentTime );
170                continue;
171            }
172            this->gameClock_->capture();
173
174            // STATISTICS
175            statisticsTickInfo tickInfo = {currentTime, 0};
176            statisticsTickTimes_.push_back(tickInfo);
177            this->periodTime_ += this->gameClock_->getDeltaTimeMicroseconds();
178
179            // UPDATE STATE STACK
180            while (this->requestedStateNodes_.size() > 1)
181            {
182                // Note: this->requestedStateNodes_.front() is the currently active state node
183                std::vector<shared_ptr<GameStateTreeNode> >::iterator it = this->requestedStateNodes_.begin() + 1;
184                if (*it == this->activeStateNode_->parent_.lock())
185                    this->unloadState(this->activeStateNode_->state_);
186                else // has to be child
187                    this->loadState((*it)->state_);
188                this->activeStateNode_ = *it;
189                this->requestedStateNodes_.erase(this->requestedStateNodes_.begin());
190            }
191
192            // UPDATE, bottom to top in the stack
193            this->core_->update(*this->gameClock_);
194            for (std::vector<GameState*>::const_iterator it = this->activeStates_.begin();
195                it != this->activeStates_.end(); ++it)
196            {
197                // Add tick time for most of the states
198                uint64_t timeBeforeTick;
199                if ((*it)->getCountTickTime())
200                    timeBeforeTick = this->gameClock_->getRealMicroseconds();
201               
202                (*it)->update(*this->gameClock_);
203
204                if ((*it)->getCountTickTime())
205                    this->addTickTime(static_cast<uint32_t>(this->gameClock_->getRealMicroseconds() - timeBeforeTick));
206            }
207
208            // STATISTICS
209            if (this->periodTime_ > statisticsRefreshCycle_)
210            {
211                std::list<statisticsTickInfo>::iterator it = this->statisticsTickTimes_.begin();
212                assert(it != this->statisticsTickTimes_.end());
213                int64_t lastTime = currentTime - this->statisticsAvgLength_;
214                if ((int64_t)it->tickTime < lastTime)
215                {
216                    do
217                    {
218                        assert(this->periodTickTime_ > it->tickLength);
219                        this->periodTickTime_ -= it->tickLength;
220                        ++it;
221                        assert(it != this->statisticsTickTimes_.end());
222                    } while ((int64_t)it->tickTime < lastTime);
223                    this->statisticsTickTimes_.erase(this->statisticsTickTimes_.begin(), it);
224                }
225
226                uint32_t framesPerPeriod = this->statisticsTickTimes_.size();
227                this->avgFPS_ = static_cast<float>(framesPerPeriod) / (currentTime - this->statisticsTickTimes_.front().tickTime) * 1000000.0f;
228                this->avgTickTime_ = static_cast<float>(this->periodTickTime_) / framesPerPeriod / 1000.0f;
229
230                this->periodTime_ -= this->statisticsRefreshCycle_;
231            }
232        }
233
234        // UNLOAD all remaining states
235        while (!this->activeStates_.empty())
236            this->unloadState(this->activeStates_.back());
237        this->activeStateNode_.reset();
238        this->requestedStateNodes_.clear();
239    }
240
241    void Game::stop()
242    {
243        this->abort_ = true;
244    }
245
246    void Game::addTickTime(uint32_t length)
247    {
248        assert(!this->statisticsTickTimes_.empty());
249        this->statisticsTickTimes_.back().tickLength += length;
250        this->periodTickTime_+=length;
251    }
252
253
254    /***** GameState related *****/
255
256    void Game::requestState(const std::string& name)
257    {
258        GameState* state = this->getState(name);
259        if (state == NULL || this->activeStateNode_ == NULL)
260            return;
261
262        shared_ptr<GameStateTreeNode> requestedNode;
263
264        // this->requestedStateNodes_.back() is the currently active state
265        shared_ptr<GameStateTreeNode> lastRequestedNode = this->requestedStateNodes_.back();
266
267        // Already the active node?
268        if (state == lastRequestedNode->state_)
269        {
270            COUT(2) << "Warning: Requesting the currently active state! Ignoring." << std::endl;
271            return;
272        }
273
274        // Check children first
275        for (unsigned int i = 0; i < lastRequestedNode->children_.size(); ++i)
276        {
277            if (lastRequestedNode->children_[i]->state_ == state)
278            {
279                requestedNode = lastRequestedNode->children_[i];
280                break;
281            }
282        }
283
284        // Check parent and all its grand parents
285        shared_ptr<GameStateTreeNode> currentNode = lastRequestedNode;
286        while (requestedNode == NULL && currentNode != NULL)
287        {
288            if (currentNode->state_ == state)
289                requestedNode = currentNode;
290            currentNode = currentNode->parent_.lock();
291        }
292
293        if (requestedNode == NULL)
294            COUT(1) << "Error: Requested GameState transition is not allowed. Ignoring." << std::endl;
295        else
296            this->requestedStateNodes_.push_back(requestedNode);
297    }
298
299    void Game::requestStates(const std::string& names)
300    {
301        SubString tokens(names, ",;", " ");
302        for (unsigned int i = 0; i < tokens.size(); ++i)
303            this->requestState(tokens[i]);
304    }
305
306    void Game::popState()
307    {
308        if (this->activeStateNode_ != NULL && this->requestedStateNodes_.back()->parent_.lock())
309            this->requestState(this->requestedStateNodes_.back()->parent_.lock()->state_->getName());
310        else
311            COUT(2) << "Warning: Could not pop GameState. Ignoring." << std::endl;
312    }
313
314    GameState* Game::getState(const std::string& name)
315    {
316        std::map<std::string, GameState*>::const_iterator it = allStates_s.find(getLowercase(name));
317        if (it != allStates_s.end())
318            return it->second;
319        else
320        {
321            COUT(1) << "Error: Could not find GameState '" << name << "'. Ignoring." << std::endl;
322            return 0;
323        }
324    }
325
326    void Game::setStateHierarchy(const std::string& str)
327    {
328        // Split string into pieces of the form whitespacesText
329        std::vector<std::pair<std::string, unsigned> > stateStrings;
330        size_t pos = 0;
331        size_t startPos = 0;
332        while (pos < str.size())
333        {
334            unsigned indentation = 0;
335            while(pos < str.size() && str[pos] == ' ')
336                ++indentation, ++pos;
337            startPos = pos;
338            while(pos < str.size() && str[pos] != ' ')
339                ++pos;
340            stateStrings.push_back(std::pair<std::string, unsigned>(
341                str.substr(startPos, pos - startPos), indentation));
342        }
343        unsigned int currentLevel = 0;
344        shared_ptr<GameStateTreeNode> currentNode;
345        for (std::vector<std::pair<std::string, unsigned> >::const_iterator it = stateStrings.begin(); it != stateStrings.end(); ++it)
346        {
347            std::string newStateName = it->first;
348            unsigned newLevel = it->second;
349            GameState* newState = this->getState(newStateName);
350            if (!newState)
351                ThrowException(GameState, std::string("GameState with name '") + newStateName + "' not found!");
352            if (newLevel == 0)
353            {
354                // root
355                if (this->rootStateNode_ != NULL)
356                    ThrowException(GameState, "No two root GameStates are allowed!");
357                shared_ptr<GameStateTreeNode> newNode(new GameStateTreeNode);
358                newNode->state_ = newState;
359                this->rootStateNode_ = newNode;
360                currentNode = this->rootStateNode_;
361            }
362            else if (currentNode)
363            {
364                shared_ptr<GameStateTreeNode> newNode(new GameStateTreeNode);
365                newNode->state_ = newState;
366                if (newLevel < currentLevel)
367                {
368                    // Get down the hierarchy
369                    do
370                        currentNode = currentNode->parent_.lock();
371                    while (newLevel < --currentLevel);
372                }
373                if (newLevel == currentLevel)
374                {
375                    // same level
376                    newNode->parent_ = currentNode->parent_;
377                    newNode->parent_.lock()->children_.push_back(newNode);
378                }
379                else if (newLevel == currentLevel + 1)
380                {
381                    // child
382                    newNode->parent_ = currentNode;
383                    currentNode->children_.push_back(newNode);
384                }
385                else
386                    ThrowException(GameState, "Indentation error while parsing the hierarchy.");
387                currentNode = newNode;
388                currentLevel = newLevel;
389            }
390            else
391            {
392                ThrowException(GameState, "No root GameState specified!");
393            }
394        }
395    }
396
397    /*** Internal ***/
398
399    void Game::loadState(GameState* state)
400    {
401        if (!this->activeStates_.empty())
402            this->activeStates_.back()->activity_.topState = false;
403        state->activate();
404        state->activity_.topState = true;
405        this->activeStates_.push_back(state);
406    }
407
408    void Game::unloadState(orxonox::GameState* state)
409    {
410        state->activity_.topState = false;
411        state->deactivate();
412        this->activeStates_.pop_back();
413        if (!this->activeStates_.empty())
414            this->activeStates_.back()->activity_.topState = true;
415    }
416
417    /*static*/ bool Game::addGameState(GameState* state)
418    {
419        std::map<std::string, GameState*>::const_iterator it = allStates_s.find(getLowercase(state->getName()));
420        if (it == allStates_s.end())
421            allStates_s[getLowercase(state->getName())] = state;
422        else
423            ThrowException(GameState, "Cannot add two GameStates with the same name to 'Game'.");
424
425        // just a required dummy return value
426        return true;
427    }
428
429    /*static*/ void Game::destroyStates()
430    {
431        // Delete all GameStates created by the macros
432        for (std::map<std::string, GameState*>::const_iterator it = allStates_s.begin(); it != allStates_s.end(); ++it)
433            delete it->second;
434        allStates_s.clear();
435    }
436}
Note: See TracBrowser for help on using the repository browser.