Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

Ignore:
Timestamp:
Jun 28, 2009, 1:47:57 PM (15 years ago)
Author:
rgrieder
Message:

Improved exception-safety in the Game class and fixed some issues and bugs resulting from the changes.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • code/branches/core4/src/core/Game.cc

    r3196 r3238  
    5454    using boost::weak_ptr;
    5555
     56    std::map<std::string, GameState*> Game::allStates_s;
     57    Game* Game::singletonRef_s = 0;
     58
    5659    static void stop_game()
    5760        { Game::getInstance().stop(); }
    5861    SetConsoleCommandShortcutExternAlias(stop_game, "exit");
     62    // Add an empty gamestate that serves as internal root state
     63    AddGameState(GameState, "emptyRootGameState");
    5964
    6065    struct _CoreExport GameStateTreeNode
     
    6469        std::vector<shared_ptr<GameStateTreeNode> > children_;
    6570    };
    66 
    67     std::map<std::string, GameState*> Game::allStates_s;
    68     Game* Game::singletonRef_s = 0;
    6971
    7072    /**
     
    7779        singletonRef_s = this;
    7880
    79         this->abort_ = false;
     81        this->bAbort_ = false;
     82        bChangingState_ = false;
     83        // The empty root state is ALWAYS loaded!
     84        this->rootStateNode_ = shared_ptr<GameStateTreeNode>(new GameStateTreeNode());
     85        this->rootStateNode_->state_ = getState("emptyRootGameState");
     86        this->activeStateNode_ = this->rootStateNode_;
     87        this->activeStates_.push_back(this->rootStateNode_->state_);
    8088
    8189        // reset statistics
     
    8694        this->avgFPS_ = 0.0f;
    8795        this->avgTickTime_ = 0.0f;
    88 
    8996
    9097        // Set up a basic clock to keep time
     
    147154    void Game::run()
    148155    {
    149         // Always start with the ROOT state
    150         this->requestedStateNodes_.push_back(this->rootStateNode_);
    151         this->activeStateNode_ = this->rootStateNode_;
    152         this->loadState(this->rootStateNode_->state_);
     156        if (this->requestedStateNodes_.empty())
     157            COUT(0) << "Warning: Starting game without requesting GameState. This automatically terminates the program." << std::endl;
    153158
    154159        // START GAME
    155160        this->gameClock_->capture(); // first delta time should be about 0 seconds
    156         while (!this->abort_ && !this->activeStates_.empty())
     161        while (!this->bAbort_ && (!this->activeStates_.empty() || this->requestedStateNodes_.size() > 0))
    157162        {
    158163            this->gameClock_->capture();
     
    165170
    166171            // UPDATE STATE STACK
    167             while (this->requestedStateNodes_.size() > 1)
    168             {
    169                 // Note: this->requestedStateNodes_.front() is the currently active state node
    170                 std::vector<shared_ptr<GameStateTreeNode> >::iterator it = this->requestedStateNodes_.begin() + 1;
    171                 if (*it == this->activeStateNode_->parent_.lock())
     172            while (this->requestedStateNodes_.size() > 0)
     173            {
     174                shared_ptr<GameStateTreeNode> requestedStateNode = this->requestedStateNodes_.front();
     175                assert(this->activeStateNode_);
     176                if (!this->activeStateNode_->parent_.expired() && requestedStateNode == this->activeStateNode_->parent_.lock())
    172177                    this->unloadState(this->activeStateNode_->state_);
    173178                else // has to be child
    174                     this->loadState((*it)->state_);
    175                 this->activeStateNode_ = *it;
     179                {
     180                    try
     181                    {
     182                        this->loadState(requestedStateNode->state_);
     183                    }
     184                    catch (const std::exception& ex)
     185                    {
     186                        COUT(1) << "Error: Loading GameState '" << requestedStateNode->state_->getName() << "' failed: " << ex.what() << std::endl;
     187                        // All scheduled operations have now been rendered inert --> flush them and issue a warning
     188                        if (this->requestedStateNodes_.size() > 1)
     189                            COUT(1) << "All " << this->requestedStateNodes_.size() - 1 << " scheduled transitions have been ignored." << std::endl;
     190                        this->requestedStateNodes_.clear();
     191                        break;
     192                    }
     193                }
     194                this->activeStateNode_ = requestedStateNode;
    176195                this->requestedStateNodes_.erase(this->requestedStateNodes_.begin());
    177196            }
    178197
    179             // UPDATE, bottom to top in the stack
    180             this->core_->update(*this->gameClock_);
    181             for (std::vector<GameState*>::const_iterator it = this->activeStates_.begin();
     198            // UPDATE, Core first
     199            try
     200            {
     201                this->core_->update(*this->gameClock_);
     202            }
     203            catch (...)
     204            {
     205                COUT(0) << "An exception occured while ticking the Core. This should really never happen!" << std::endl;
     206                COUT(0) << "Closing the program." << std::endl;
     207                this->stop();
     208                break;
     209            }
     210
     211            // UPDATE, GameStates bottom to top in the stack
     212            // Note: The first element is the empty root state, which doesn't need ticking
     213            for (std::vector<GameState*>::const_iterator it = this->activeStates_.begin() + 1;
    182214                it != this->activeStates_.end(); ++it)
    183215            {
    184                 // Add tick time for most of the states
    185                 uint64_t timeBeforeTick;
    186                 if ((*it)->getCountTickTime())
    187                     timeBeforeTick = this->gameClock_->getRealMicroseconds();
    188                
    189                 (*it)->update(*this->gameClock_);
    190 
    191                 if ((*it)->getCountTickTime())
    192                     this->addTickTime(static_cast<uint32_t>(this->gameClock_->getRealMicroseconds() - timeBeforeTick));
     216                try
     217                {
     218                    // Add tick time for most of the states
     219                    uint64_t timeBeforeTick;
     220                    if (!(*it)->ignoreTickTime())
     221                        timeBeforeTick = this->gameClock_->getRealMicroseconds();
     222                    (*it)->update(*this->gameClock_);
     223                    if (!(*it)->ignoreTickTime())
     224                        this->addTickTime(static_cast<uint32_t>(this->gameClock_->getRealMicroseconds() - timeBeforeTick));
     225                }
     226                catch (...)
     227                {
     228                    COUT(1) << "An exception occured while ticking GameState '" << (*it)->getName() << "'. This should really never happen!" << std::endl;
     229                    COUT(1) << "Unloading all GameStates depending on the one that crashed." << std::endl;
     230                    if ((*it)->getParent() != NULL)
     231                        this->requestState((*it)->getParent()->getName());
     232                    else
     233                        this->stop();
     234                    break;
     235                }
     236
    193237            }
    194238
     
    203247                    do
    204248                    {
    205                         assert(this->periodTickTime_ > it->tickLength);
     249                        assert(this->periodTickTime_ >= it->tickLength);
    206250                        this->periodTickTime_ -= it->tickLength;
    207251                        ++it;
     
    220264
    221265        // UNLOAD all remaining states
    222         while (!this->activeStates_.empty())
     266        while (this->activeStates_.size() > 1)
    223267            this->unloadState(this->activeStates_.back());
    224         this->activeStateNode_.reset();
     268        this->activeStateNode_ = this->rootStateNode_;
    225269        this->requestedStateNodes_.clear();
    226270    }
     
    228272    void Game::stop()
    229273    {
    230         this->abort_ = true;
     274        this->bAbort_ = true;
    231275    }
    232276
     
    244288    {
    245289        GameState* state = this->getState(name);
    246         if (state == NULL || this->activeStateNode_ == NULL)
     290        if (state == NULL)
    247291            return;
    248292
    249         shared_ptr<GameStateTreeNode> requestedNode;
    250 
    251         // this->requestedStateNodes_.back() is the currently active state
    252         shared_ptr<GameStateTreeNode> lastRequestedNode = this->requestedStateNodes_.back();
    253 
    254         // Already the active node?
     293        //if (this->bChangingState_)
     294        //{
     295        //    COUT(2) << "Warning: Requesting GameStates while loading/unloading a GameState is illegal! Ignoring." << std::endl;
     296        //    return;
     297        //}
     298
     299        shared_ptr<GameStateTreeNode> lastRequestedNode;
     300        if (this->requestedStateNodes_.empty())
     301            lastRequestedNode = this->activeStateNode_;
     302        else
     303            lastRequestedNode = this->requestedStateNodes_.back();
    255304        if (state == lastRequestedNode->state_)
    256305        {
     
    260309
    261310        // Check children first
     311        shared_ptr<GameStateTreeNode> requestedNode;
    262312        for (unsigned int i = 0; i < lastRequestedNode->children_.size(); ++i)
    263313        {
     
    293343    void Game::popState()
    294344    {
    295         if (this->activeStateNode_ != NULL && this->requestedStateNodes_.back()->parent_.lock())
    296             this->requestState(this->requestedStateNodes_.back()->parent_.lock()->state_->getName());
    297         else
    298             COUT(2) << "Warning: Could not pop GameState. Ignoring." << std::endl;
     345        shared_ptr<GameStateTreeNode> lastRequestedNode;
     346        if (this->requestedStateNodes_.empty())
     347            lastRequestedNode = this->activeStateNode_;
     348        else
     349            lastRequestedNode = this->requestedStateNodes_.back();
     350        if (lastRequestedNode != this->rootStateNode_)
     351            this->requestState(lastRequestedNode->parent_.lock()->state_->getName());
     352        else
     353            COUT(2) << "Warning: Can't pop the internal dummy root GameState" << std::endl;
    299354    }
    300355
     
    325380            while(pos < str.size() && str[pos] != ' ')
    326381                ++pos;
    327             stateStrings.push_back(std::pair<std::string, unsigned>(
    328                 str.substr(startPos, pos - startPos), indentation));
     382            stateStrings.push_back(std::make_pair(str.substr(startPos, pos - startPos), indentation));
    329383        }
    330384        unsigned int currentLevel = 0;
    331         shared_ptr<GameStateTreeNode> currentNode;
     385        shared_ptr<GameStateTreeNode> currentNode = this->rootStateNode_;
    332386        for (std::vector<std::pair<std::string, unsigned> >::const_iterator it = stateStrings.begin(); it != stateStrings.end(); ++it)
    333387        {
    334388            std::string newStateName = it->first;
    335             unsigned newLevel = it->second;
     389            unsigned newLevel = it->second + 1; // empty root is 0
    336390            GameState* newState = this->getState(newStateName);
    337391            if (!newState)
    338                 ThrowException(GameState, std::string("GameState with name '") + newStateName + "' not found!");
    339             if (newLevel == 0)
    340             {
    341                 // root
    342                 if (this->rootStateNode_ != NULL)
    343                     ThrowException(GameState, "No two root GameStates are allowed!");
    344                 shared_ptr<GameStateTreeNode> newNode(new GameStateTreeNode);
    345                 newNode->state_ = newState;
    346                 this->rootStateNode_ = newNode;
    347                 currentNode = this->rootStateNode_;
    348             }
    349             else if (currentNode)
    350             {
    351                 shared_ptr<GameStateTreeNode> newNode(new GameStateTreeNode);
    352                 newNode->state_ = newState;
    353                 if (newLevel < currentLevel)
    354                 {
    355                     // Get down the hierarchy
    356                     do
    357                         currentNode = currentNode->parent_.lock();
    358                     while (newLevel < --currentLevel);
    359                 }
    360                 if (newLevel == currentLevel)
    361                 {
    362                     // same level
    363                     newNode->parent_ = currentNode->parent_;
    364                     newNode->parent_.lock()->children_.push_back(newNode);
    365                 }
    366                 else if (newLevel == currentLevel + 1)
    367                 {
    368                     // child
    369                     newNode->parent_ = currentNode;
    370                     currentNode->children_.push_back(newNode);
    371                 }
    372                 else
    373                     ThrowException(GameState, "Indentation error while parsing the hierarchy.");
    374                 currentNode = newNode;
    375                 currentLevel = newLevel;
     392                ThrowException(GameState, "GameState with name '" << newStateName << "' not found!");
     393            if (newState == this->rootStateNode_->state_)
     394                ThrowException(GameState, "You shouldn't use 'emptyRootGameState' in the hierarchy...");
     395            shared_ptr<GameStateTreeNode> newNode(new GameStateTreeNode);
     396            newNode->state_ = newState;
     397
     398            if (newLevel <= currentLevel)
     399            {
     400                do
     401                    currentNode = currentNode->parent_.lock();
     402                while (newLevel <= --currentLevel);
     403            }
     404            if (newLevel == currentLevel + 1)
     405            {
     406                // Add the child
     407                newNode->parent_ = currentNode;
     408                currentNode->children_.push_back(newNode);
     409                currentNode->state_->addChild(newNode->state_);
    376410            }
    377411            else
    378             {
    379                 ThrowException(GameState, "No root GameState specified!");
    380             }
     412                ThrowException(GameState, "Indentation error while parsing the hierarchy.");
     413            currentNode = newNode;
     414            currentLevel = newLevel;
    381415        }
    382416    }
     
    386420    void Game::loadState(GameState* state)
    387421    {
     422        this->bChangingState_ = true;
     423        state->activate();
    388424        if (!this->activeStates_.empty())
    389425            this->activeStates_.back()->activity_.topState = false;
    390         state->activate();
     426        this->activeStates_.push_back(state);
    391427        state->activity_.topState = true;
    392         this->activeStates_.push_back(state);
     428        this->bChangingState_ = false;
    393429    }
    394430
    395431    void Game::unloadState(orxonox::GameState* state)
    396432    {
     433        this->bChangingState_ = true;
    397434        state->activity_.topState = false;
    398         state->deactivate();
    399435        this->activeStates_.pop_back();
    400436        if (!this->activeStates_.empty())
    401437            this->activeStates_.back()->activity_.topState = true;
     438        try
     439        {
     440            state->deactivate();
     441        }
     442        catch (const std::exception& ex)
     443        {
     444            COUT(2) << "Warning: Unloading GameState '" << state->getName() << "' threw an exception: " << ex.what() << std::endl;
     445            COUT(2) << "         There might be potential resource leaks involved! To avoid this, improve exception-safety." << std::endl;
     446        }
     447        this->bChangingState_ = false;
    402448    }
    403449
Note: See TracChangeset for help on using the changeset viewer.