Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/netp3/src/core/Game.cc @ 3050

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

Fixed tick-time issue: Not all tick times were being taken into account.
As of now, only GSRoot and GSGraphics manage the tick times themselves (marked with "AddGameState(GSRoot, "root", false)")

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