Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/core7/src/libraries/core/Game.cc @ 10468

Last change on this file since 10468 was 10380, checked in by landauf, 10 years ago

registered missing classes.
WorldEntityCollisionShape is the only class which was really missing, all other classes are singletons or part of the framework.

  • Property svn:eol-style set to native
File size: 25.0 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#include <loki/ScopeGuard.h>
40
41#include "util/Clock.h"
42#include "util/Output.h"
43#include "util/Exception.h"
44#include "util/Sleep.h"
45#include "util/SubString.h"
46#include "Core.h"
47#include "CoreIncludes.h"
48#include "commandline/CommandLineParser.h"
49#include "config/ConfigValueIncludes.h"
50#include "GameMode.h"
51#include "GameState.h"
52#include "GraphicsManager.h"
53#include "GUIManager.h"
54#include "command/ConsoleCommandIncludes.h"
55
56namespace orxonox
57{
58    static void stop_game()
59        { Game::getInstance().stop(); }
60    SetConsoleCommand("exit", &stop_game);
61    static void printFPS()
62        { orxout(message) << Game::getInstance().getAvgFPS() << endl; }
63    SetConsoleCommand("Stats", "printFPS", &printFPS);
64    static void printTickTime()
65        { orxout(message) << Game::getInstance().getAvgTickTime() << endl; }
66    SetConsoleCommand("Stats", "printTickTime", &printTickTime);
67
68    std::map<std::string, GameStateInfo> Game::gameStateDeclarations_s;
69    Game* Game::singletonPtr_s = 0;
70
71    //! Represents one node of the game state tree.
72    struct GameStateTreeNode
73    {
74        std::string name_;
75        weak_ptr<GameStateTreeNode> parent_;
76        std::vector<shared_ptr<GameStateTreeNode> > children_;
77    };
78
79    RegisterAbstractClass(Game).inheritsFrom<Configurable>();
80
81    Game::Game(const std::string& cmdLine)
82        : gameClock_(NULL)
83        , core_(NULL)
84        , bChangingState_(false)
85        , bAbort_(false)
86        , destructionHelper_(this)
87    {
88        orxout(internal_status) << "initializing Game object..." << endl;
89
90#ifdef ORXONOX_PLATFORM_WINDOWS
91        minimumSleepTime_ = 1000/*us*/;
92#else
93        minimumSleepTime_ = 0/*us*/;
94#endif
95
96        // reset statistics
97        this->statisticsStartTime_ = 0;
98        this->statisticsTickTimes_.clear();
99        this->periodTickTime_ = 0;
100        this->periodTime_ = 0;
101        this->avgFPS_ = 0.0f;
102        this->avgTickTime_ = 0.0f;
103        this->excessSleepTime_ = 0;
104
105        // Create an empty root state
106        this->declareGameState<GameState>("GameState", "emptyRootGameState", true, false);
107
108        // Set up a basic clock to keep time
109        this->gameClock_ = new Clock();
110
111        // Create the Core
112        orxout(internal_info) << "creating Core object:" << endl;
113        this->core_ = new Core(cmdLine);
114
115        // Do this after the Core creation!
116        RegisterObject(Game);
117        this->setConfigValues();
118
119        // After the core has been created, we can safely instantiate the GameStates that don't require graphics
120        for (std::map<std::string, GameStateInfo>::const_iterator it = gameStateDeclarations_s.begin();
121            it != gameStateDeclarations_s.end(); ++it)
122        {
123            if (!it->second.bGraphicsMode)
124                constructedStates_[it->second.stateName] = GameStateFactory::fabricate(it->second);
125        }
126
127        // The empty root state is ALWAYS loaded!
128        this->rootStateNode_ = shared_ptr<GameStateTreeNode>(new GameStateTreeNode());
129        this->rootStateNode_->name_ = "emptyRootGameState";
130        this->loadedTopStateNode_ = this->rootStateNode_;
131        this->loadedStates_.push_back(this->getState(rootStateNode_->name_));
132
133        orxout(internal_status) << "finished initializing Game object" << endl;
134    }
135
136    void Game::destroy()
137    {
138        orxout(internal_status) << "destroying Game object..." << endl;
139
140        // Remove us from the object lists again to avoid problems when destroying them
141        this->unregisterObject();
142
143        assert(loadedStates_.size() <= 1); // Just empty root GameState
144        // Destroy all GameStates (shared_ptrs take care of actual destruction)
145        constructedStates_.clear();
146
147        GameStateFactory::getFactories().clear();
148        safeObjectDelete(&core_);
149        safeObjectDelete(&gameClock_);
150
151        orxout(internal_status) << "finished destroying Game object..." << endl;
152    }
153
154    void Game::setConfigValues()
155    {
156        SetConfigValue(statisticsRefreshCycle_, 250000)
157            .description("Sets the time in microseconds interval at which average fps, etc. get updated.");
158        SetConfigValue(statisticsAvgLength_, 1000000)
159            .description("Sets the time in microseconds interval at which average fps, etc. gets calculated.");
160
161        SetConfigValueExternal(fpsLimit_, "GraphicsSettings", "fpsLimit", 50)
162            .description("Sets the desired frame rate (0 for no limit).");
163    }
164
165    /**
166    @brief
167        Main loop of the orxonox game.
168    @note
169        We use the Ogre::Timer to measure time since it uses the most precise
170        method an any platform (however the windows timer lacks time when under
171        heavy kernel load!).
172    */
173    void Game::run()
174    {
175        if (this->requestedStateNodes_.empty())
176            orxout(user_error) << "Starting game without requesting GameState. This automatically terminates the program." << endl;
177
178        // Update the GameState stack if required. We do this already here to have a properly initialized game before entering the main loop
179        this->updateGameStateStack();
180
181        orxout(user_status) << "Game loaded" << endl;
182        orxout(internal_status) << "-------------------- starting main loop --------------------" << endl;
183
184        // START GAME
185        // first delta time should be about 0 seconds
186        this->gameClock_->capture();
187        // A first item is required for the fps limiter
188        StatisticsTickInfo tickInfo = {0, 0};
189        statisticsTickTimes_.push_back(tickInfo);
190        while (!this->bAbort_ && (!this->loadedStates_.empty() || this->requestedStateNodes_.size() > 0))
191        {
192            // Generate the dt
193            this->gameClock_->capture();
194
195            // Statistics init
196            StatisticsTickInfo tickInfo = {gameClock_->getMicroseconds(), 0};
197            statisticsTickTimes_.push_back(tickInfo);
198            this->periodTime_ += this->gameClock_->getDeltaTimeMicroseconds();
199
200            // Update the GameState stack if required
201            this->updateGameStateStack();
202
203            // Core preUpdate
204            try
205                { this->core_->preUpdate(*this->gameClock_); }
206            catch (...)
207            {
208                orxout(user_error) << "An exception occurred in the Core preUpdate: " << Exception::handleMessage() << endl;
209                orxout(user_error) << "This should really never happen! Closing the program." << endl;
210                this->stop();
211                break;
212            }
213
214            // Update the GameStates bottom up in the stack
215            this->updateGameStates();
216
217            // Core postUpdate
218            try
219                { this->core_->postUpdate(*this->gameClock_); }
220            catch (...)
221            {
222                orxout(user_error) << "An exception occurred in the Core postUpdate: " << Exception::handleMessage() << endl;
223                orxout(user_error) << "This should really never happen! Closing the program." << endl;
224                this->stop();
225                break;
226            }
227
228            // Evaluate statistics
229            this->updateStatistics();
230
231            // Limit frame rate
232            static bool hasVSync = GameMode::showsGraphics() && GraphicsManager::getInstance().hasVSyncEnabled(); // can be static since changes of VSync currently require a restart
233            if (this->fpsLimit_ > 0 && !hasVSync)
234                this->updateFPSLimiter();
235        }
236
237        orxout(internal_status) << "-------------------- finished main loop --------------------" << endl;
238
239        // UNLOAD all remaining states
240        while (this->loadedStates_.size() > 1)
241            this->unloadState(this->loadedStates_.back()->getName());
242        this->loadedTopStateNode_ = this->rootStateNode_;
243        this->requestedStateNodes_.clear();
244    }
245
246    void Game::updateGameStateStack()
247    {
248        while (this->requestedStateNodes_.size() > 0)
249        {
250            shared_ptr<GameStateTreeNode> requestedStateNode = this->requestedStateNodes_.front();
251            assert(this->loadedTopStateNode_);
252            if (!this->loadedTopStateNode_->parent_.expired() && requestedStateNode == this->loadedTopStateNode_->parent_.lock())
253                this->unloadState(loadedTopStateNode_->name_);
254            else // has to be child
255            {
256                try
257                {
258                    this->loadState(requestedStateNode->name_);
259                }
260                catch (...)
261                {
262                    orxout(user_error) << "Loading GameState '" << requestedStateNode->name_ << "' failed: " << Exception::handleMessage() << endl;
263                    // All scheduled operations have now been rendered inert --> flush them and issue a warning
264                    if (this->requestedStateNodes_.size() > 1)
265                        orxout(internal_info) << "All " << this->requestedStateNodes_.size() - 1 << " scheduled transitions have been ignored." << endl;
266                    this->requestedStateNodes_.clear();
267                    break;
268                }
269            }
270            this->loadedTopStateNode_ = requestedStateNode;
271            this->requestedStateNodes_.erase(this->requestedStateNodes_.begin());
272        }
273    }
274
275    void Game::updateGameStates()
276    {
277        // Note: The first element is the empty root state, which doesn't need ticking
278        for (GameStateVector::const_iterator it = this->loadedStates_.begin() + 1;
279            it != this->loadedStates_.end(); ++it)
280        {
281            try
282            {
283                // Add tick time for most of the states
284                uint64_t timeBeforeTick = 0;
285                if ((*it)->getInfo().bIgnoreTickTime)
286                    timeBeforeTick = this->gameClock_->getRealMicroseconds();
287                (*it)->update(*this->gameClock_);
288                if ((*it)->getInfo().bIgnoreTickTime)
289                    this->subtractTickTime(static_cast<int32_t>(this->gameClock_->getRealMicroseconds() - timeBeforeTick));
290            }
291            catch (...)
292            {
293                orxout(user_error) << "An exception occurred while updating '" << (*it)->getName() << "': " << Exception::handleMessage() << endl;
294                orxout(user_error) << "This should really never happen!" << endl;
295                orxout(user_error) << "Unloading all GameStates depending on the one that crashed." << endl;
296                shared_ptr<GameStateTreeNode> current = this->loadedTopStateNode_;
297                while (current->name_ != (*it)->getName() && current)
298                    current = current->parent_.lock();
299                if (current && current->parent_.lock())
300                    this->requestState(current->parent_.lock()->name_);
301                else
302                    this->stop();
303                break;
304            }
305        }
306    }
307
308    void Game::updateStatistics()
309    {
310        // Add the tick time of this frame (rendering time has already been subtracted)
311        uint64_t currentTime = gameClock_->getMicroseconds();
312        uint64_t currentRealTime = gameClock_->getRealMicroseconds();
313        this->statisticsTickTimes_.back().tickLength += (uint32_t)(currentRealTime - currentTime);
314        this->periodTickTime_ += (uint32_t)(currentRealTime - currentTime);
315        if (this->periodTime_ > this->statisticsRefreshCycle_)
316        {
317            std::list<StatisticsTickInfo>::iterator it = this->statisticsTickTimes_.begin();
318            assert(it != this->statisticsTickTimes_.end());
319            int64_t lastTime = currentTime - this->statisticsAvgLength_;
320            if (static_cast<int64_t>(it->tickTime) < lastTime)
321            {
322                do
323                {
324                    assert(this->periodTickTime_ >= it->tickLength);
325                    this->periodTickTime_ -= it->tickLength;
326                    ++it;
327                    assert(it != this->statisticsTickTimes_.end());
328                } while (static_cast<int64_t>(it->tickTime) < lastTime);
329                this->statisticsTickTimes_.erase(this->statisticsTickTimes_.begin(), it);
330            }
331
332            uint32_t framesPerPeriod = this->statisticsTickTimes_.size();
333            // Why minus 1? No idea, but otherwise the fps rate is always (from 10 to 200!) one frame too low
334            this->avgFPS_ = -1 + static_cast<float>(framesPerPeriod) / (currentTime - this->statisticsTickTimes_.front().tickTime) * 1000000.0f;
335            this->avgTickTime_ = static_cast<float>(this->periodTickTime_) / framesPerPeriod / 1000.0f;
336
337            this->periodTime_ -= this->statisticsRefreshCycle_;
338        }
339    }
340
341    void Game::updateFPSLimiter()
342    {
343        uint64_t nextTime = gameClock_->getMicroseconds() - excessSleepTime_ + static_cast<uint32_t>(1000000.0f / fpsLimit_);
344        uint64_t currentRealTime = gameClock_->getRealMicroseconds();
345        while (currentRealTime < nextTime - minimumSleepTime_)
346        {
347            usleep((unsigned long)(nextTime - currentRealTime));
348            currentRealTime = gameClock_->getRealMicroseconds();
349        }
350        // Integrate excess to avoid steady state error
351        excessSleepTime_ = (int)(currentRealTime - nextTime);
352        // Anti windup
353        if (excessSleepTime_ > 50000) // 20ms is about the maximum time Windows would sleep for too long
354            excessSleepTime_ = 50000;
355    }
356
357    void Game::stop()
358    {
359        orxout(user_status) << "Exit" << endl;
360        this->bAbort_ = true;
361    }
362
363    void Game::subtractTickTime(int32_t length)
364    {
365        assert(!this->statisticsTickTimes_.empty());
366        this->statisticsTickTimes_.back().tickLength -= length;
367        this->periodTickTime_ -= length;
368    }
369
370
371    /***** GameState related *****/
372
373    void Game::requestState(const std::string& name)
374    {
375        if (!this->checkState(name))
376        {
377            orxout(user_warning) << "GameState named '" << name << "' doesn't exist!" << endl;
378            return;
379        }
380
381        if (this->bChangingState_)
382        {
383            orxout(user_warning) << "Requesting GameStates while loading/unloading a GameState is illegal! Ignoring." << endl;
384            return;
385        }
386
387        shared_ptr<GameStateTreeNode> lastRequestedNode;
388        if (this->requestedStateNodes_.empty())
389            lastRequestedNode = this->loadedTopStateNode_;
390        else
391            lastRequestedNode = this->requestedStateNodes_.back();
392        if (name == lastRequestedNode->name_)
393        {
394            orxout(user_warning) << "Requesting the currently active state! Ignoring." << endl;
395            return;
396        }
397
398        // Check children first
399        std::vector<shared_ptr<GameStateTreeNode> > requestedNodes;
400        for (unsigned int i = 0; i < lastRequestedNode->children_.size(); ++i)
401        {
402            if (lastRequestedNode->children_[i]->name_ == name)
403            {
404                requestedNodes.push_back(lastRequestedNode->children_[i]);
405                break;
406            }
407        }
408
409        if (requestedNodes.empty())
410        {
411            // Check parent and all its grand parents
412            shared_ptr<GameStateTreeNode> currentNode = lastRequestedNode;
413            while (currentNode != NULL)
414            {
415                if (currentNode->name_ == name)
416                    break;
417                currentNode = currentNode->parent_.lock();
418                requestedNodes.push_back(currentNode);
419            }
420            if (currentNode == NULL)
421                requestedNodes.clear();
422        }
423
424        if (requestedNodes.empty())
425            orxout(user_error) << "Requested GameState transition is not allowed. Ignoring." << endl;
426        else
427            this->requestedStateNodes_.insert(requestedStateNodes_.end(), requestedNodes.begin(), requestedNodes.end());
428    }
429
430    void Game::requestStates(const std::string& names)
431    {
432        SubString tokens(names, ",;", " ");
433        for (unsigned int i = 0; i < tokens.size(); ++i)
434            this->requestState(tokens[i]);
435    }
436
437    void Game::popState()
438    {
439        shared_ptr<GameStateTreeNode> lastRequestedNode;
440        if (this->requestedStateNodes_.empty())
441            lastRequestedNode = this->loadedTopStateNode_;
442        else
443            lastRequestedNode = this->requestedStateNodes_.back();
444        if (lastRequestedNode != this->rootStateNode_)
445            this->requestState(lastRequestedNode->parent_.lock()->name_);
446        else
447            orxout(internal_warning) << "Can't pop the internal dummy root GameState" << endl;
448    }
449
450    shared_ptr<GameState> Game::getState(const std::string& name)
451    {
452        GameStateMap::const_iterator it = constructedStates_.find(name);
453        if (it != constructedStates_.end())
454            return it->second;
455        else
456        {
457            std::map<std::string, GameStateInfo>::const_iterator it = gameStateDeclarations_s.find(name);
458            if (it != gameStateDeclarations_s.end())
459                orxout(internal_error) << "GameState '" << name << "' has not yet been loaded." << endl;
460            else
461                orxout(internal_error) << "Could not find GameState '" << name << "'." << endl;
462            return shared_ptr<GameState>();
463        }
464    }
465
466    void Game::setStateHierarchy(const std::string& str)
467    {
468        // Split string into pieces of the form whitespacesText
469        std::vector<std::pair<std::string, int> > stateStrings;
470        size_t pos = 0;
471        size_t startPos = 0;
472        while (pos < str.size())
473        {
474            int indentation = 0;
475            while (pos < str.size() && str[pos] == ' ')
476                ++indentation, ++pos;
477            startPos = pos;
478            while (pos < str.size() && str[pos] != ' ')
479                ++pos;
480            stateStrings.push_back(std::make_pair(str.substr(startPos, pos - startPos), indentation));
481        }
482        if (stateStrings.empty())
483            ThrowException(GameState, "Emtpy GameState hierarchy provided, terminating.");
484        // Add element with large identation to detect the last with just an iterator
485        stateStrings.push_back(std::make_pair(std::string(), -1));
486
487        // Parse elements recursively
488        std::vector<std::pair<std::string, int> >::const_iterator begin = stateStrings.begin();
489        parseStates(begin, this->rootStateNode_);
490    }
491
492    /*** Internal ***/
493
494    void Game::parseStates(std::vector<std::pair<std::string, int> >::const_iterator& it, shared_ptr<GameStateTreeNode> currentNode)
495    {
496        SubString tokens(it->first, ",");
497        std::vector<std::pair<std::string, int> >::const_iterator startIt = it;
498
499        for (unsigned int i = 0; i < tokens.size(); ++i)
500        {
501            it = startIt; // Reset iterator to the beginning of the sub tree
502            if (!this->checkState(tokens[i]))
503                ThrowException(GameState, "GameState with name '" << tokens[i] << "' not found!");
504            if (tokens[i] == this->rootStateNode_->name_)
505                ThrowException(GameState, "You shouldn't use 'emptyRootGameState' in the hierarchy...");
506            shared_ptr<GameStateTreeNode> node(new GameStateTreeNode());
507            node->name_ = tokens[i];
508            node->parent_ = currentNode;
509            currentNode->children_.push_back(node);
510
511            int currentLevel = it->second;
512            ++it;
513            while (it->second != -1)
514            {
515                if (it->second <= currentLevel)
516                    break;
517                else if (it->second == currentLevel + 1)
518                    parseStates(it, node);
519                else
520                    ThrowException(GameState, "Indentation error while parsing the hierarchy.");
521            }
522        }
523    }
524
525    void Game::loadGraphics()
526    {
527        if (!GameMode::showsGraphics())
528        {
529            orxout(user_status) << "Loading graphics" << endl;
530            orxout(internal_info) << "loading graphics in Game" << endl;
531
532            core_->loadGraphics();
533            Loki::ScopeGuard graphicsUnloader = Loki::MakeObjGuard(*this, &Game::unloadGraphics);
534
535            // Construct all the GameStates that require graphics
536            for (std::map<std::string, GameStateInfo>::const_iterator it = gameStateDeclarations_s.begin();
537                it != gameStateDeclarations_s.end(); ++it)
538            {
539                if (it->second.bGraphicsMode)
540                {
541                    // Game state loading failure is serious --> don't catch
542                    shared_ptr<GameState> gameState = GameStateFactory::fabricate(it->second);
543                    if (!constructedStates_.insert(std::make_pair(
544                        it->second.stateName, gameState)).second)
545                        assert(false); // GameState was already created!
546                }
547            }
548            graphicsUnloader.Dismiss();
549
550            orxout(internal_info) << "finished loading graphics in Game" << endl;
551        }
552    }
553
554    void Game::unloadGraphics()
555    {
556        if (GameMode::showsGraphics())
557        {
558            orxout(user_status) << "Unloading graphics" << endl;
559            orxout(internal_info) << "unloading graphics in Game" << endl;
560
561            // Destroy all the GameStates that require graphics
562            for (GameStateMap::iterator it = constructedStates_.begin(); it != constructedStates_.end();)
563            {
564                if (it->second->getInfo().bGraphicsMode)
565                    constructedStates_.erase(it++);
566                else
567                    ++it;
568            }
569
570            core_->unloadGraphics();
571        }
572    }
573
574    bool Game::checkState(const std::string& name) const
575    {
576        std::map<std::string, GameStateInfo>::const_iterator it = gameStateDeclarations_s.find(name);
577        if (it == gameStateDeclarations_s.end())
578            return false;
579        else
580            return true;
581    }
582
583    void Game::loadState(const std::string& name)
584    {
585        orxout(internal_status) << "loading state '" << name << "'" << endl;
586
587        this->bChangingState_ = true;
588        LOKI_ON_BLOCK_EXIT_OBJ(*this, &Game::resetChangingState); (void)LOKI_ANONYMOUS_VARIABLE(scopeGuard);
589
590        // If state requires graphics, load it
591        Loki::ScopeGuard graphicsUnloader = Loki::MakeObjGuard(*this, &Game::unloadGraphics);
592        if (gameStateDeclarations_s[name].bGraphicsMode && !GameMode::showsGraphics())
593            this->loadGraphics();
594        else
595            graphicsUnloader.Dismiss();
596
597        shared_ptr<GameState> state = this->getState(name);
598        state->activateInternal();
599        if (!this->loadedStates_.empty())
600            this->loadedStates_.back()->activity_.topState = false;
601        this->loadedStates_.push_back(state);
602        state->activity_.topState = true;
603
604        graphicsUnloader.Dismiss();
605    }
606
607    void Game::unloadState(const std::string& name)
608    {
609        orxout(internal_status) << "unloading state '" << name << "'" << endl;
610
611        this->bChangingState_ = true;
612        try
613        {
614            shared_ptr<GameState> state = this->getState(name);
615            state->activity_.topState = false;
616            this->loadedStates_.pop_back();
617            if (!this->loadedStates_.empty())
618                this->loadedStates_.back()->activity_.topState = true;
619            state->deactivateInternal();
620        }
621        catch (...)
622        {
623            orxout(internal_warning) << "Unloading GameState '" << name << "' threw an exception: " << Exception::handleMessage() << endl;
624            orxout(internal_warning) << "There might be potential resource leaks involved! To avoid this, improve exception-safety." << endl;
625        }
626        // Check if graphics is still required
627        if (!bAbort_)
628        {
629            bool graphicsRequired = false;
630            for (unsigned i = 0; i < loadedStates_.size(); ++i)
631                graphicsRequired |= loadedStates_[i]->getInfo().bGraphicsMode;
632            if (!graphicsRequired)
633                this->unloadGraphics();
634        }
635        this->bChangingState_ = false;
636    }
637
638    /*static*/ std::map<std::string, shared_ptr<Game::GameStateFactory> >& Game::GameStateFactory::getFactories()
639    {
640        static std::map<std::string, shared_ptr<GameStateFactory> > factories;
641        return factories;
642    }
643
644    /*static*/ shared_ptr<GameState> Game::GameStateFactory::fabricate(const GameStateInfo& info)
645    {
646        std::map<std::string, shared_ptr<Game::GameStateFactory> >::const_iterator it = getFactories().find(info.className);
647        assert(it != getFactories().end());
648        return it->second->fabricateInternal(info);
649    }
650}
Note: See TracBrowser for help on using the repository browser.