Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

merged netp3 branch back to trunk

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