Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/gui/src/core/Game.cc @ 2846

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

Moving game clock from Core to Game.
Other small fixes.

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