Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

Ignore:
Timestamp:
Jul 30, 2009, 2:10:44 PM (15 years ago)
Author:
rgrieder
Message:

Merged resource branch back to the trunk. Changes:

  • Automated graphics loading by evaluating whether a GameState requires it
  • Using native Tcl library (x3n)

Windows users: Update your dependency package!

Location:
code/trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • code/trunk

  • code/trunk/src/core/Game.cc

    r3323 r3370  
    4040#include "util/Debug.h"
    4141#include "util/Exception.h"
     42#include "util/ScopeGuard.h"
    4243#include "util/Sleep.h"
    4344#include "util/SubString.h"
     
    4849#include "CoreIncludes.h"
    4950#include "ConfigValueIncludes.h"
     51#include "GameMode.h"
    5052#include "GameState.h"
    5153
     
    5961    SetConsoleCommandShortcutExternAlias(stop_game, "exit");
    6062
    61     std::map<std::string, Game::GameStateInfo> Game::gameStateDeclarations_s;
    62     Game* Game::singletonRef_s = 0;
     63    std::map<std::string, GameStateInfo> Game::gameStateDeclarations_s;
     64    Game* Game::singletonPtr_s = 0;
    6365
    6466
     
    6971    struct GameStateTreeNode
    7072    {
    71         GameState* state_;
     73        std::string name_;
    7274        weak_ptr<GameStateTreeNode> parent_;
    7375        std::vector<shared_ptr<GameStateTreeNode> > children_;
     
    112114    Game::Game(const std::string& cmdLine)
    113115    {
    114         if (singletonRef_s != 0)
    115         {
    116             COUT(0) << "Error: The Game singleton cannot be recreated! Shutting down." << std::endl;
    117             abort();
    118         }
    119         singletonRef_s = this;
    120 
    121116        this->bAbort_ = false;
    122117        bChangingState_ = false;
    123118
     119#ifdef ORXONOX_PLATFORM_WINDOWS
     120        minimumSleepTime_ = 1000/*us*/;
     121#else
     122        minimumSleepTime_ = 0/*us*/;
     123#endif
     124
    124125        // Create an empty root state
    125         declareGameState<GameState>("GameState", "emptyRootGameState", true, false);
    126 
    127         // reset statistics
    128         this->statisticsStartTime_ = 0;
    129         this->statisticsTickTimes_.clear();
    130         this->periodTickTime_ = 0;
    131         this->periodTime_ = 0;
    132         this->avgFPS_ = 0.0f;
    133         this->avgTickTime_ = 0.0f;
     126        this->declareGameState<GameState>("GameState", "emptyRootGameState", true, false);
    134127
    135128        // Set up a basic clock to keep time
    136         this->gameClock_ = new Clock();
     129        this->gameClock_.reset(new Clock());
    137130
    138131        // Create the Core
    139         this->core_ = new Core(cmdLine);
    140 
    141         // After the core has been created, we can safely instantiate the GameStates
     132        this->core_.reset(new Core(cmdLine));
     133
     134        // After the core has been created, we can safely instantiate the GameStates that don't require graphics
    142135        for (std::map<std::string, GameStateInfo>::const_iterator it = gameStateDeclarations_s.begin();
    143136            it != gameStateDeclarations_s.end(); ++it)
    144137        {
    145             // Only create the states appropriate for the game mode
    146             //if (GameMode::showsGraphics || !it->second.bGraphicsMode)
    147             GameStateConstrParams params = { it->second.stateName, it->second.bIgnoreTickTime };
    148             gameStates_[getLowercase(it->second.stateName)] = GameStateFactory::fabricate(it->second.className, params);
     138            if (!it->second.bGraphicsMode)
     139                constructedStates_[it->second.stateName] = GameStateFactory::fabricate(it->second);
    149140        }
    150141
    151142        // The empty root state is ALWAYS loaded!
    152143        this->rootStateNode_ = shared_ptr<GameStateTreeNode>(new GameStateTreeNode());
    153         this->rootStateNode_->state_ = getState("emptyRootGameState");
    154         this->activeStateNode_ = this->rootStateNode_;
    155         this->activeStates_.push_back(this->rootStateNode_->state_);
     144        this->rootStateNode_->name_ = "emptyRootGameState";
     145        this->loadedTopStateNode_ = this->rootStateNode_;
     146        this->loadedStates_.push_back(this->getState(rootStateNode_->name_));
    156147
    157148        // Do this after the Core creation!
    158         this->configuration_ = new GameConfiguration();
     149        this->configuration_.reset(new GameConfiguration());
    159150    }
    160151
    161152    /**
    162153    @brief
     154        All destruction code is handled by scoped_ptrs and SimpleScopeGuards.
    163155    */
    164156    Game::~Game()
    165157    {
    166         // Destroy the configuration helper class instance
    167         delete this->configuration_;
    168 
    169         // Destroy the GameStates (note that the nodes still point to them, but doesn't matter)
    170         for (std::map<std::string, GameState*>::const_iterator it = gameStates_.begin();
    171             it != gameStates_.end(); ++it)
    172             delete it->second;
    173 
    174         // Destroy the Core and with it almost everything
    175         delete this->core_;
    176         delete this->gameClock_;
    177 
    178         // Take care of the GameStateFactories
    179         GameStateFactory::destroyFactories();
    180 
    181         // Don't assign singletonRef_s with NULL! Recreation is not supported
    182158    }
    183159
     
    195171            COUT(0) << "Warning: Starting game without requesting GameState. This automatically terminates the program." << std::endl;
    196172
     173        // reset statistics
     174        this->statisticsStartTime_ = 0;
     175        this->statisticsTickTimes_.clear();
     176        this->periodTickTime_ = 0;
     177        this->periodTime_ = 0;
     178        this->avgFPS_ = 0.0f;
     179        this->avgTickTime_ = 0.0f;
     180        this->excessSleepTime_ = 0;
     181
    197182        // START GAME
    198183        // first delta time should be about 0 seconds
     
    201186        StatisticsTickInfo tickInfo = {0, 0};
    202187        statisticsTickTimes_.push_back(tickInfo);
    203         while (!this->bAbort_ && (!this->activeStates_.empty() || this->requestedStateNodes_.size() > 0))
    204         {
    205             uint64_t currentTime = this->gameClock_->getRealMicroseconds();
    206 
    207             uint64_t nextTickTime = statisticsTickTimes_.back().tickTime + static_cast<uint64_t>(1000000.0f / configuration_->fpsLimit_);
    208             if (currentTime < nextTickTime)
    209             {
    210                 usleep(nextTickTime - currentTime);
    211                 continue;
    212             }
     188        while (!this->bAbort_ && (!this->loadedStates_.empty() || this->requestedStateNodes_.size() > 0))
     189        {
     190            // Generate the dt
    213191            this->gameClock_->capture();
    214192
    215             // STATISTICS
    216             StatisticsTickInfo tickInfo = {currentTime, 0};
     193            // Statistics init
     194            StatisticsTickInfo tickInfo = {gameClock_->getMicroseconds(), 0};
    217195            statisticsTickTimes_.push_back(tickInfo);
    218196            this->periodTime_ += this->gameClock_->getDeltaTimeMicroseconds();
    219197
    220             // UPDATE STATE STACK
    221             while (this->requestedStateNodes_.size() > 0)
    222             {
    223                 shared_ptr<GameStateTreeNode> requestedStateNode = this->requestedStateNodes_.front();
    224                 assert(this->activeStateNode_);
    225                 if (!this->activeStateNode_->parent_.expired() && requestedStateNode == this->activeStateNode_->parent_.lock())
    226                     this->unloadState(this->activeStateNode_->state_);
    227                 else // has to be child
    228                 {
    229                     try
    230                     {
    231                         this->loadState(requestedStateNode->state_);
    232                     }
    233                     catch (const std::exception& ex)
    234                     {
    235                         COUT(1) << "Error: Loading GameState '" << requestedStateNode->state_->getName() << "' failed: " << ex.what() << std::endl;
    236                         // All scheduled operations have now been rendered inert --> flush them and issue a warning
    237                         if (this->requestedStateNodes_.size() > 1)
    238                             COUT(1) << "All " << this->requestedStateNodes_.size() - 1 << " scheduled transitions have been ignored." << std::endl;
    239                         this->requestedStateNodes_.clear();
    240                         break;
    241                     }
    242                 }
    243                 this->activeStateNode_ = requestedStateNode;
    244                 this->requestedStateNodes_.erase(this->requestedStateNodes_.begin());
    245             }
    246 
    247             // UPDATE, Core first
    248             bool threwException = false;
    249             try
    250             {
    251                 this->core_->update(*this->gameClock_);
    252             }
    253             catch (const std::exception& ex)
    254             {
    255                 threwException = true;
    256                 COUT(0) << "Exception while ticking the Core: " << ex.what() << std::endl;
    257             }
    258             catch (...)
    259             {
    260                 threwException = true;
    261             }
    262             if (threwException)
    263             {
    264                 COUT(0) << "An exception occured while ticking the Core. This should really never happen!" << std::endl;
    265                 COUT(0) << "Closing the program." << std::endl;
     198            // Update the GameState stack if required
     199            this->updateGameStateStack();
     200
     201            // Core preUpdate (doesn't throw)
     202            if (!this->core_->preUpdate(*this->gameClock_))
     203            {
    266204                this->stop();
    267205                break;
    268206            }
    269207
    270             // UPDATE, GameStates bottom to top in the stack
    271             // Note: The first element is the empty root state, which doesn't need ticking
    272             for (std::vector<GameState*>::const_iterator it = this->activeStates_.begin() + 1;
    273                 it != this->activeStates_.end(); ++it)
    274             {
    275                 bool threwException = false;
     208            // Update the GameStates bottom up in the stack
     209            this->updateGameStates();
     210
     211            // Core postUpdate (doesn't throw)
     212            if (!this->core_->postUpdate(*this->gameClock_))
     213            {
     214                this->stop();
     215                break;
     216            }
     217
     218            // Evaluate statistics
     219            this->updateStatistics();
     220
     221            // Limit framerate
     222            this->updateFPSLimiter();
     223        }
     224
     225        // UNLOAD all remaining states
     226        while (this->loadedStates_.size() > 1)
     227            this->unloadState(this->loadedStates_.back()->getName());
     228        this->loadedTopStateNode_ = this->rootStateNode_;
     229        this->requestedStateNodes_.clear();
     230    }
     231
     232    void Game::updateGameStateStack()
     233    {
     234        while (this->requestedStateNodes_.size() > 0)
     235        {
     236            shared_ptr<GameStateTreeNode> requestedStateNode = this->requestedStateNodes_.front();
     237            assert(this->loadedTopStateNode_);
     238            if (!this->loadedTopStateNode_->parent_.expired() && requestedStateNode == this->loadedTopStateNode_->parent_.lock())
     239                this->unloadState(loadedTopStateNode_->name_);
     240            else // has to be child
     241            {
    276242                try
    277243                {
    278                     // Add tick time for most of the states
    279                     uint64_t timeBeforeTick;
    280                     if (!(*it)->ignoreTickTime())
    281                         timeBeforeTick = this->gameClock_->getRealMicroseconds();
    282                     (*it)->update(*this->gameClock_);
    283                     if (!(*it)->ignoreTickTime())
    284                         this->addTickTime(static_cast<uint32_t>(this->gameClock_->getRealMicroseconds() - timeBeforeTick));
     244                    this->loadState(requestedStateNode->name_);
    285245                }
    286246                catch (const std::exception& ex)
    287247                {
    288                     threwException = true;
    289                     COUT(0) << "Exception while ticking: " << ex.what() << std::endl;
    290                 }
    291                 catch (...)
    292                 {
    293                     threwException = true;
    294                 }
    295                 if (threwException)
    296                 {
    297                     COUT(1) << "An exception occured while ticking GameState '" << (*it)->getName() << "'. This should really never happen!" << std::endl;
    298                     COUT(1) << "Unloading all GameStates depending on the one that crashed." << std::endl;
    299                     if ((*it)->getParent() != NULL)
    300                         this->requestState((*it)->getParent()->getName());
    301                     else
    302                         this->stop();
     248                    COUT(1) << "Error: Loading GameState '" << requestedStateNode->name_ << "' failed: " << ex.what() << std::endl;
     249                    // All scheduled operations have now been rendered inert --> flush them and issue a warning
     250                    if (this->requestedStateNodes_.size() > 1)
     251                        COUT(1) << "All " << this->requestedStateNodes_.size() - 1 << " scheduled transitions have been ignored." << std::endl;
     252                    this->requestedStateNodes_.clear();
    303253                    break;
    304254                }
    305 
    306             }
    307 
    308             // STATISTICS
    309             if (this->periodTime_ > this->configuration_->statisticsRefreshCycle_)
    310             {
    311                 std::list<StatisticsTickInfo>::iterator it = this->statisticsTickTimes_.begin();
    312                 assert(it != this->statisticsTickTimes_.end());
    313                 int64_t lastTime = currentTime - this->configuration_->statisticsAvgLength_;
    314                 if (static_cast<int64_t>(it->tickTime) < lastTime)
     255            }
     256            this->loadedTopStateNode_ = requestedStateNode;
     257            this->requestedStateNodes_.erase(this->requestedStateNodes_.begin());
     258        }
     259    }
     260
     261    void Game::updateGameStates()
     262    {
     263        // Note: The first element is the empty root state, which doesn't need ticking
     264        for (GameStateVector::const_iterator it = this->loadedStates_.begin() + 1;
     265            it != this->loadedStates_.end(); ++it)
     266        {
     267            std::string exceptionMessage;
     268            try
     269            {
     270                // Add tick time for most of the states
     271                uint64_t timeBeforeTick;
     272                if ((*it)->getInfo().bIgnoreTickTime)
     273                    timeBeforeTick = this->gameClock_->getRealMicroseconds();
     274                (*it)->update(*this->gameClock_);
     275                if ((*it)->getInfo().bIgnoreTickTime)
     276                    this->subtractTickTime(static_cast<int32_t>(this->gameClock_->getRealMicroseconds() - timeBeforeTick));
     277            }
     278            catch (const std::exception& ex)
     279            { exceptionMessage = ex.what(); }
     280            catch (...)
     281            { exceptionMessage = "Unknown exception"; }
     282            if (!exceptionMessage.empty())
     283            {
     284                COUT(1) << "An exception occurred while updating '" << (*it)->getName() << "': " << exceptionMessage << std::endl;
     285                COUT(1) << "This should really never happen!" << std::endl;
     286                COUT(1) << "Unloading all GameStates depending on the one that crashed." << std::endl;
     287                shared_ptr<GameStateTreeNode> current = this->loadedTopStateNode_;
     288                while (current->name_ != (*it)->getName() && current)
     289                    current = current->parent_.lock();
     290                if (current && current->parent_.lock())
     291                    this->requestState(current->parent_.lock()->name_);
     292                else
     293                    this->stop();
     294                break;
     295            }
     296        }
     297    }
     298
     299    void Game::updateStatistics()
     300    {
     301        // Add the tick time of this frame (rendering time has already been subtracted)
     302        uint64_t currentTime = gameClock_->getMicroseconds();
     303        uint64_t currentRealTime = gameClock_->getRealMicroseconds();
     304        this->statisticsTickTimes_.back().tickLength += currentRealTime - currentTime;
     305        this->periodTickTime_ += currentRealTime - currentTime;
     306        if (this->periodTime_ > this->configuration_->statisticsRefreshCycle_)
     307        {
     308            std::list<StatisticsTickInfo>::iterator it = this->statisticsTickTimes_.begin();
     309            assert(it != this->statisticsTickTimes_.end());
     310            int64_t lastTime = currentTime - this->configuration_->statisticsAvgLength_;
     311            if (static_cast<int64_t>(it->tickTime) < lastTime)
     312            {
     313                do
    315314                {
    316                     do
    317                     {
    318                         assert(this->periodTickTime_ >= it->tickLength);
    319                         this->periodTickTime_ -= it->tickLength;
    320                         ++it;
    321                         assert(it != this->statisticsTickTimes_.end());
    322                     } while (static_cast<int64_t>(it->tickTime) < lastTime);
    323                     this->statisticsTickTimes_.erase(this->statisticsTickTimes_.begin(), it);
    324                 }
    325 
    326                 uint32_t framesPerPeriod = this->statisticsTickTimes_.size();
    327                 this->avgFPS_ = static_cast<float>(framesPerPeriod) / (currentTime - this->statisticsTickTimes_.front().tickTime) * 1000000.0f;
    328                 this->avgTickTime_ = static_cast<float>(this->periodTickTime_) / framesPerPeriod / 1000.0f;
    329 
    330                 this->periodTime_ -= this->configuration_->statisticsRefreshCycle_;
    331             }
    332         }
    333 
    334         // UNLOAD all remaining states
    335         while (this->activeStates_.size() > 1)
    336             this->unloadState(this->activeStates_.back());
    337         this->activeStateNode_ = this->rootStateNode_;
    338         this->requestedStateNodes_.clear();
     315                    assert(this->periodTickTime_ >= it->tickLength);
     316                    this->periodTickTime_ -= it->tickLength;
     317                    ++it;
     318                    assert(it != this->statisticsTickTimes_.end());
     319                } while (static_cast<int64_t>(it->tickTime) < lastTime);
     320                this->statisticsTickTimes_.erase(this->statisticsTickTimes_.begin(), it);
     321            }
     322
     323            uint32_t framesPerPeriod = this->statisticsTickTimes_.size();
     324            this->avgFPS_ = static_cast<float>(framesPerPeriod) / (currentTime - this->statisticsTickTimes_.front().tickTime) * 1000000.0f;
     325            this->avgTickTime_ = static_cast<float>(this->periodTickTime_) / framesPerPeriod / 1000.0f;
     326
     327            this->periodTime_ -= this->configuration_->statisticsRefreshCycle_;
     328        }
     329    }
     330
     331    void Game::updateFPSLimiter()
     332    {
     333        // Why configuration_->fpsLimit_ - 1? No idea, but otherwise the fps rate is always (from 10 to 200!) one frame too high
     334        uint32_t nextTime = gameClock_->getMicroseconds() - excessSleepTime_ + static_cast<uint32_t>(1000000.0f / (configuration_->fpsLimit_ - 1));
     335        uint64_t currentRealTime = gameClock_->getRealMicroseconds();
     336        while (currentRealTime < nextTime - minimumSleepTime_)
     337        {
     338            usleep(nextTime - currentRealTime);
     339            currentRealTime = gameClock_->getRealMicroseconds();
     340        }
     341        // Integrate excess to avoid steady state error
     342        excessSleepTime_ = currentRealTime - nextTime;
     343        // Anti windup
     344        if (excessSleepTime_ > 50000) // 20ms is about the maximum time Windows would sleep for too long
     345            excessSleepTime_ = 50000;
    339346    }
    340347
     
    344351    }
    345352
    346     void Game::addTickTime(uint32_t length)
     353    void Game::subtractTickTime(int32_t length)
    347354    {
    348355        assert(!this->statisticsTickTimes_.empty());
    349         this->statisticsTickTimes_.back().tickLength += length;
    350         this->periodTickTime_+=length;
     356        this->statisticsTickTimes_.back().tickLength -= length;
     357        this->periodTickTime_ -= length;
    351358    }
    352359
     
    356363    void Game::requestState(const std::string& name)
    357364    {
    358         GameState* state = this->getState(name);
    359         if (state == NULL)
     365        if (!this->checkState(name))
     366        {
     367            COUT(2) << "Warning: GameState named '" << name << "' doesn't exist!" << std::endl;
    360368            return;
    361 
    362         //if (this->bChangingState_)
    363         //{
    364         //    COUT(2) << "Warning: Requesting GameStates while loading/unloading a GameState is illegal! Ignoring." << std::endl;
    365         //    return;
    366         //}
     369        }
     370
     371        if (this->bChangingState_)
     372        {
     373            COUT(2) << "Warning: Requesting GameStates while loading/unloading a GameState is illegal! Ignoring." << std::endl;
     374            return;
     375        }
    367376
    368377        shared_ptr<GameStateTreeNode> lastRequestedNode;
    369378        if (this->requestedStateNodes_.empty())
    370             lastRequestedNode = this->activeStateNode_;
     379            lastRequestedNode = this->loadedTopStateNode_;
    371380        else
    372381            lastRequestedNode = this->requestedStateNodes_.back();
    373         if (state == lastRequestedNode->state_)
     382        if (name == lastRequestedNode->name_)
    374383        {
    375384            COUT(2) << "Warning: Requesting the currently active state! Ignoring." << std::endl;
     
    381390        for (unsigned int i = 0; i < lastRequestedNode->children_.size(); ++i)
    382391        {
    383             if (lastRequestedNode->children_[i]->state_ == state)
     392            if (lastRequestedNode->children_[i]->name_ == name)
    384393            {
    385394                requestedNodes.push_back(lastRequestedNode->children_[i]);
     
    394403            while (currentNode != NULL)
    395404            {
    396                 if (currentNode->state_ == state)
     405                if (currentNode->name_ == name)
    397406                    break;
    398407                currentNode = currentNode->parent_.lock();
     
    418427        shared_ptr<GameStateTreeNode> lastRequestedNode;
    419428        if (this->requestedStateNodes_.empty())
    420             lastRequestedNode = this->activeStateNode_;
     429            lastRequestedNode = this->loadedTopStateNode_;
    421430        else
    422431            lastRequestedNode = this->requestedStateNodes_.back();
    423432        if (lastRequestedNode != this->rootStateNode_)
    424             this->requestState(lastRequestedNode->parent_.lock()->state_->getName());
     433            this->requestState(lastRequestedNode->parent_.lock()->name_);
    425434        else
    426435            COUT(2) << "Warning: Can't pop the internal dummy root GameState" << std::endl;
    427436    }
    428437
    429     GameState* Game::getState(const std::string& name)
    430     {
    431         std::map<std::string, GameState*>::const_iterator it = gameStates_.find(getLowercase(name));
    432         if (it != gameStates_.end())
     438    shared_ptr<GameState> Game::getState(const std::string& name)
     439    {
     440        GameStateMap::const_iterator it = constructedStates_.find(name);
     441        if (it != constructedStates_.end())
    433442            return it->second;
    434443        else
    435444        {
    436             COUT(1) << "Error: Could not find GameState '" << name << "'. Ignoring." << std::endl;
    437             return 0;
     445            std::map<std::string, GameStateInfo>::const_iterator it = gameStateDeclarations_s.find(name);
     446            if (it != gameStateDeclarations_s.end())
     447                COUT(1) << "Error: GameState '" << name << "' has not yet been loaded." << std::endl;
     448            else
     449                COUT(1) << "Error: Could not find GameState '" << name << "'." << std::endl;
     450            return shared_ptr<GameState>();
    438451        }
    439452    }
     
    461474            std::string newStateName = it->first;
    462475            unsigned newLevel = it->second + 1; // empty root is 0
    463             GameState* newState = this->getState(newStateName);
    464             if (!newState)
     476            if (!this->checkState(newStateName))
    465477                ThrowException(GameState, "GameState with name '" << newStateName << "' not found!");
    466             if (newState == this->rootStateNode_->state_)
     478            if (newStateName == this->rootStateNode_->name_)
    467479                ThrowException(GameState, "You shouldn't use 'emptyRootGameState' in the hierarchy...");
    468480            shared_ptr<GameStateTreeNode> newNode(new GameStateTreeNode);
    469             newNode->state_ = newState;
     481            newNode->name_ = newStateName;
    470482
    471483            if (newLevel <= currentLevel)
     
    480492                newNode->parent_ = currentNode;
    481493                currentNode->children_.push_back(newNode);
    482                 currentNode->state_->addChild(newNode->state_);
    483494            }
    484495            else
     
    491502    /*** Internal ***/
    492503
    493     void Game::loadState(GameState* state)
     504    void Game::loadGraphics()
     505    {
     506        if (!GameMode::bShowsGraphics_s)
     507        {
     508            core_->loadGraphics();
     509            Loki::ScopeGuard graphicsUnloader = Loki::MakeObjGuard(*this, &Game::unloadGraphics);
     510            GameMode::bShowsGraphics_s = true;
     511
     512            // Construct all the GameStates that require graphics
     513            for (std::map<std::string, GameStateInfo>::const_iterator it = gameStateDeclarations_s.begin();
     514                it != gameStateDeclarations_s.end(); ++it)
     515            {
     516                if (it->second.bGraphicsMode)
     517                {
     518                    // Game state loading failure is serious --> don't catch
     519                    shared_ptr<GameState> gameState = GameStateFactory::fabricate(it->second);
     520                    if (!constructedStates_.insert(std::make_pair(
     521                        it->second.stateName, gameState)).second)
     522                        assert(false); // GameState was already created!
     523                }
     524            }
     525            graphicsUnloader.Dismiss();
     526        }
     527    }
     528
     529    void Game::unloadGraphics()
     530    {
     531        if (GameMode::bShowsGraphics_s)
     532        {
     533            // Destroy all the GameStates that require graphics
     534            for (GameStateMap::iterator it = constructedStates_.begin(); it != constructedStates_.end();)
     535            {
     536                if (it->second->getInfo().bGraphicsMode)
     537                    constructedStates_.erase(it++);
     538                else
     539                    ++it;
     540            }
     541
     542            core_->unloadGraphics();
     543            GameMode::bShowsGraphics_s = false;
     544        }
     545    }
     546
     547    bool Game::checkState(const std::string& name) const
     548    {
     549        std::map<std::string, GameStateInfo>::const_iterator it = gameStateDeclarations_s.find(name);
     550        if (it == gameStateDeclarations_s.end())
     551            return false;
     552        else
     553            return true;
     554    }
     555
     556    void Game::loadState(const std::string& name)
    494557    {
    495558        this->bChangingState_ = true;
     559        LOKI_ON_BLOCK_EXIT_OBJ(*this, &Game::resetChangingState);
     560
     561        // If state requires graphics, load it
     562        Loki::ScopeGuard graphicsUnloader = Loki::MakeObjGuard(*this, &Game::unloadGraphics);
     563        if (gameStateDeclarations_s[name].bGraphicsMode && !GameMode::showsGraphics())
     564            this->loadGraphics();
     565        else
     566            graphicsUnloader.Dismiss();
     567
     568        shared_ptr<GameState> state = this->getState(name);
    496569        state->activate();
    497         if (!this->activeStates_.empty())
    498             this->activeStates_.back()->activity_.topState = false;
    499         this->activeStates_.push_back(state);
     570        if (!this->loadedStates_.empty())
     571            this->loadedStates_.back()->activity_.topState = false;
     572        this->loadedStates_.push_back(state);
    500573        state->activity_.topState = true;
     574
     575        graphicsUnloader.Dismiss();
     576    }
     577
     578    void Game::unloadState(const std::string& name)
     579    {
     580        this->bChangingState_ = true;
     581        try
     582        {
     583            shared_ptr<GameState> state = this->getState(name);
     584            state->activity_.topState = false;
     585            this->loadedStates_.pop_back();
     586            if (!this->loadedStates_.empty())
     587                this->loadedStates_.back()->activity_.topState = true;
     588            state->deactivate();
     589        }
     590        catch (const std::exception& ex)
     591        {
     592            COUT(2) << "Warning: Unloading GameState '" << name << "' threw an exception: " << ex.what() << std::endl;
     593            COUT(2) << "         There might be potential resource leaks involved! To avoid this, improve exception-safety." << std::endl;
     594        }
     595        // Check if graphics is still required
     596        bool graphicsRequired = false;
     597        for (unsigned i = 0; i < loadedStates_.size(); ++i)
     598            graphicsRequired |= loadedStates_[i]->getInfo().bGraphicsMode;
     599        if (!graphicsRequired)
     600            this->unloadGraphics();
    501601        this->bChangingState_ = false;
    502602    }
    503603
    504     void Game::unloadState(orxonox::GameState* state)
    505     {
    506         this->bChangingState_ = true;
    507         state->activity_.topState = false;
    508         this->activeStates_.pop_back();
    509         if (!this->activeStates_.empty())
    510             this->activeStates_.back()->activity_.topState = true;
    511         try
    512         {
    513             state->deactivate();
    514         }
    515         catch (const std::exception& ex)
    516         {
    517             COUT(2) << "Warning: Unloading GameState '" << state->getName() << "' threw an exception: " << ex.what() << std::endl;
    518             COUT(2) << "         There might be potential resource leaks involved! To avoid this, improve exception-safety." << std::endl;
    519         }
    520         this->bChangingState_ = false;
    521     }
    522 
    523     std::map<std::string, Game::GameStateFactory*> Game::GameStateFactory::factories_s;
    524 
    525     /*static*/ GameState* Game::GameStateFactory::fabricate(const std::string& className, const GameStateConstrParams& params)
    526     {
    527         std::map<std::string, GameStateFactory*>::const_iterator it = factories_s.find(className);
     604    std::map<std::string, shared_ptr<Game::GameStateFactory> > Game::GameStateFactory::factories_s;
     605
     606    /*static*/ shared_ptr<GameState> Game::GameStateFactory::fabricate(const GameStateInfo& info)
     607    {
     608        std::map<std::string, shared_ptr<Game::GameStateFactory> >::const_iterator it = factories_s.find(info.className);
    528609        assert(it != factories_s.end());
    529         return it->second->fabricate(params);
    530     }
    531 
    532     /*static*/ void Game::GameStateFactory::destroyFactories()
    533     {
    534         for (std::map<std::string, GameStateFactory*>::const_iterator it = factories_s.begin(); it != factories_s.end(); ++it)
    535             delete it->second;
    536         factories_s.clear();
     610        return it->second->fabricateInternal(info);
    537611    }
    538612}
Note: See TracChangeset for help on using the changeset viewer.