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.

Location:
code/branches/core4/src
Files:
9 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
  • code/branches/core4/src/core/Game.h

    r3196 r3238  
    118118        Clock*                          gameClock_;
    119119
    120         bool                            abort_;
     120        bool                            bChangingState_;
     121        bool                            bAbort_;
    121122
    122123        // variables for time statistics
  • code/branches/core4/src/core/GameState.cc

    r3196 r3238  
    4545        Constructor only initialises variables and sets the name permanently.
    4646    */
    47     GameState::GameState(const std::string& name, bool countTickTime)
     47    GameState::GameState(const std::string& name, bool ignoreTickTime)
    4848        : name_(name)
    49         , bCountTickTime_(countTickTime)
     49        , bIgnoreTickTime_(ignoreTickTime)
    5050        , parent_(0)
    5151    {
  • code/branches/core4/src/core/GameState.h

    r3196 r3238  
    7777
    7878    public:
    79         GameState(const std::string& name, bool countTicktime = true);
     79        GameState(const std::string& name, bool ignoreTicktime = false);
    8080        virtual ~GameState();
    8181
     
    8484        GameState* getParent()       const { return this->parent_; }
    8585
    86         bool getCountTickTime()      const { return this->bCountTickTime_; }
     86        bool ignoreTickTime()        const { return this->bIgnoreTickTime_; }
    8787
    8888        void addChild(GameState* state);
     
    9090
    9191    protected:
    92         virtual void activate() = 0;
    93         virtual void deactivate() = 0;
    94         virtual void update(const Clock& time) = 0;
     92        virtual void activate() { }
     93        virtual void deactivate() { }
     94        virtual void update(const Clock& time) { }
    9595
    9696    private:
     
    103103        const std::string                        name_;
    104104        State                                    activity_;
    105         const bool                               bCountTickTime_;
     105        const bool                               bIgnoreTickTime_;
    106106        GameState*                               parent_;
    107107        std::map<std::string, GameState*>        children_;
  • code/branches/core4/src/orxonox/Main.cc

    r3196 r3238  
    6868        );
    6969
     70        orxonox.requestState("root");
    7071        orxonox.run();
    7172
  • code/branches/core4/src/orxonox/gamestates/GSGraphics.cc

    r3196 r3238  
    5757namespace orxonox
    5858{
    59     AddGameState(GSGraphics, "graphics", false);
     59    AddGameState(GSGraphics, "graphics", true);
    6060
    6161    GSGraphics::GSGraphics(const std::string& name, bool countTickTime)
     
    198198    void GSGraphics::toggleGUI()
    199199    {
    200             GUIManager::getInstance().executeCode("toggleGUI()");
     200        GUIManager::getInstance().executeCode("toggleGUI()");
    201201    }
    202202
  • code/branches/core4/src/orxonox/gamestates/GSRoot.cc

    r3196 r3238  
    4040namespace orxonox
    4141{
    42     AddGameState(GSRoot, "root", false);
     42    AddGameState(GSRoot, "root", true);
    4343    SetCommandLineSwitch(console);
    4444    // Shortcuts for easy direct loading
  • code/branches/core4/src/util/Exception.cc

    r3196 r3238  
    6464            std::ostringstream fullDesc;
    6565
    66             fullDesc << this->getTypeName() << "_EXCEPTION";
     66            fullDesc << this->getTypeName() << "Exception";
    6767
    6868            if (this->filename_ != "")
     
    8787        return fullDescription_;
    8888    }
     89
     90    //! Returns the error description
     91    const char* Exception::what() const throw()
     92    {
     93        return getDescription().c_str();
     94    }
    8995}
  • code/branches/core4/src/util/Exception.h

    r3196 r3238  
    6868        //! Needed for compatibility with std::exception
    6969        virtual ~Exception() throw() { }
     70        const char* what() const throw();
    7071
    7172        //! Returns a full description with type, line, file and function
     
    8182        //! Returns the filename in which the exception occurred.
    8283        virtual const std::string& getFilename()        const { return this->filename_; }
    83 
    84         //! Returns a full description of the error.
    85         const char* what() const throw() { return getFullDescription().c_str(); }
    8684
    8785    protected:
     
    152150    Exception description as string
    153151*/
    154 #define ThrowException(type, description, ...) \
     152#define ThrowException(type, description) \
    155153    throw orxonox::exceptionThrowerHelper(type##Exception(static_cast<std::ostringstream&>(std::ostringstream().flush() << description).str(), __LINE__, __FILE__, __FUNCTIONNAME__))
    156154
Note: See TracChangeset for help on using the changeset viewer.