Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/core/Game.cc @ 3222

Last change on this file since 3222 was 3196, checked in by rgrieder, 15 years ago

Merged pch branch back to trunk.

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