Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/pch/src/core/Game.cc @ 3152

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

Using smart pointers in Game to manage the little tree. It's merely a test case.

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