Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

Ignore:
Timestamp:
Jul 12, 2009, 11:58:01 PM (15 years ago)
Author:
rgrieder
Message:

Merged most of the core4 revisions back to the trunk except for:

  • orxonox_cast
  • all the radical changes in the input library
Location:
code/trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • code/trunk

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

    r3196 r3280  
    5858    SetConsoleCommandShortcutExternAlias(stop_game, "exit");
    5959
    60     struct _CoreExport GameStateTreeNode
     60    std::map<std::string, Game::GameStateInfo> Game::gameStateDeclarations_s;
     61    Game* Game::singletonRef_s = 0;
     62
     63
     64    /**
     65    @brief
     66        Represents one node of the game state tree.
     67    */
     68    struct GameStateTreeNode
    6169    {
    6270        GameState* state_;
     
    6573    };
    6674
    67     std::map<std::string, GameState*> Game::allStates_s;
    68     Game* Game::singletonRef_s = 0;
     75
     76    /**
     77    @brief
     78        Another helper class for the Game singleton: we cannot derive
     79        Game from OrxonoxClass because we need to handle the Identifier
     80        destruction in the Core destructor.
     81    */
     82    class GameConfiguration : public OrxonoxClass
     83    {
     84    public:
     85        GameConfiguration()
     86        {
     87            RegisterRootObject(GameConfiguration);
     88            this->setConfigValues();
     89        }
     90
     91        void setConfigValues()
     92        {
     93            SetConfigValue(statisticsRefreshCycle_, 250000)
     94                .description("Sets the time in microseconds interval at which average fps, etc. get updated.");
     95            SetConfigValue(statisticsAvgLength_, 1000000)
     96                .description("Sets the time in microseconds interval at which average fps, etc. gets calculated.");
     97        }
     98
     99        unsigned int statisticsRefreshCycle_;
     100        unsigned int statisticsAvgLength_;
     101    };
     102
    69103
    70104    /**
     
    74108    Game::Game(int argc, char** argv)
    75109    {
    76         assert(singletonRef_s == 0);
     110        if (singletonRef_s != 0)
     111        {
     112            COUT(0) << "Error: The Game singleton cannot be recreated! Shutting down." << std::endl;
     113            abort();
     114        }
    77115        singletonRef_s = this;
    78116
    79         this->abort_ = false;
     117        this->bAbort_ = false;
     118        bChangingState_ = false;
     119
     120        // Create an empty root state
     121        declareGameState<GameState>("GameState", "emptyRootGameState", true, false);
    80122
    81123        // reset statistics
     
    87129        this->avgTickTime_ = 0.0f;
    88130
    89 
    90131        // Set up a basic clock to keep time
    91132        this->gameClock_ = new Clock();
    92133
    93         this->core_ = new orxonox::Core();
    94         this->core_->initialise(argc, argv);
    95 
    96         RegisterRootObject(Game);
    97         this->setConfigValues();
     134        // Create the Core
     135        this->core_ = new Core(argc, argv);
     136
     137        // After the core has been created, we can safely instantiate the GameStates
     138        for (std::map<std::string, GameStateInfo>::const_iterator it = gameStateDeclarations_s.begin();
     139            it != gameStateDeclarations_s.end(); ++it)
     140        {
     141            // Only create the states appropriate for the game mode
     142            //if (GameMode::showsGraphics || !it->second.bGraphicsMode)
     143            GameStateConstrParams params = { it->second.stateName, it->second.bIgnoreTickTime };
     144            gameStates_[getLowercase(it->second.stateName)] = GameStateFactory::fabricate(it->second.className, params);
     145        }
     146
     147        // The empty root state is ALWAYS loaded!
     148        this->rootStateNode_ = shared_ptr<GameStateTreeNode>(new GameStateTreeNode());
     149        this->rootStateNode_->state_ = getState("emptyRootGameState");
     150        this->activeStateNode_ = this->rootStateNode_;
     151        this->activeStates_.push_back(this->rootStateNode_->state_);
     152
     153        // Do this after the Core creation!
     154        this->configuration_ = new GameConfiguration();
    98155    }
    99156
     
    103160    Game::~Game()
    104161    {
    105         // Destroy pretty much everyhting left
     162        // Destroy the configuration helper class instance
     163        delete this->configuration_;
     164
     165        // Destroy the GameStates (note that the nodes still point to them, but doesn't matter)
     166        for (std::map<std::string, GameState*>::const_iterator it = gameStates_.begin();
     167            it != gameStates_.end(); ++it)
     168            delete it->second;
     169
     170        // Destroy the Core and with it almost everything
    106171        delete this->core_;
    107 
    108172        delete this->gameClock_;
    109173
    110         assert(singletonRef_s);
    111         singletonRef_s = 0;
    112     }
    113 
    114     void Game::setConfigValues()
    115     {
    116         SetConfigValue(statisticsRefreshCycle_, 250000)
    117             .description("Sets the time in microseconds interval at which average fps, etc. get updated.");
    118         SetConfigValue(statisticsAvgLength_, 1000000)
    119             .description("Sets the time in microseconds interval at which average fps, etc. gets calculated.");
    120         SetConfigValue(levelName_, "presentation_dm.oxw")
    121             .description("Sets the preselection of the level in the main menu.");
    122     }
    123 
    124     void Game::setLevel(std::string levelName)
    125     {
    126         ModifyConfigValue(levelName_, set, levelName);
    127     }
    128 
    129     std::string Game::getLevel()
    130     {
    131         std::string levelName;
    132         CommandLine::getValue("level", &levelName);
    133         if (levelName == "")
    134             return levelName_;
    135         else
    136             return levelName;
     174        // Take care of the GameStateFactories
     175        GameStateFactory::destroyFactories();
     176
     177        // Don't assign singletonRef_s with NULL! Recreation is not supported
    137178    }
    138179
     
    147188    void Game::run()
    148189    {
    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_);
     190        if (this->requestedStateNodes_.empty())
     191            COUT(0) << "Warning: Starting game without requesting GameState. This automatically terminates the program." << std::endl;
    153192
    154193        // START GAME
    155194        this->gameClock_->capture(); // first delta time should be about 0 seconds
    156         while (!this->abort_ && !this->activeStates_.empty())
     195        while (!this->bAbort_ && (!this->activeStates_.empty() || this->requestedStateNodes_.size() > 0))
    157196        {
    158197            this->gameClock_->capture();
     
    160199
    161200            // STATISTICS
    162             statisticsTickInfo tickInfo = {currentTime, 0};
     201            StatisticsTickInfo tickInfo = {currentTime, 0};
    163202            statisticsTickTimes_.push_back(tickInfo);
    164203            this->periodTime_ += this->gameClock_->getDeltaTimeMicroseconds();
    165204
    166205            // 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())
     206            while (this->requestedStateNodes_.size() > 0)
     207            {
     208                shared_ptr<GameStateTreeNode> requestedStateNode = this->requestedStateNodes_.front();
     209                assert(this->activeStateNode_);
     210                if (!this->activeStateNode_->parent_.expired() && requestedStateNode == this->activeStateNode_->parent_.lock())
    172211                    this->unloadState(this->activeStateNode_->state_);
    173212                else // has to be child
    174                     this->loadState((*it)->state_);
    175                 this->activeStateNode_ = *it;
     213                {
     214                    try
     215                    {
     216                        this->loadState(requestedStateNode->state_);
     217                    }
     218                    catch (const std::exception& ex)
     219                    {
     220                        COUT(1) << "Error: Loading GameState '" << requestedStateNode->state_->getName() << "' failed: " << ex.what() << std::endl;
     221                        // All scheduled operations have now been rendered inert --> flush them and issue a warning
     222                        if (this->requestedStateNodes_.size() > 1)
     223                            COUT(1) << "All " << this->requestedStateNodes_.size() - 1 << " scheduled transitions have been ignored." << std::endl;
     224                        this->requestedStateNodes_.clear();
     225                        break;
     226                    }
     227                }
     228                this->activeStateNode_ = requestedStateNode;
    176229                this->requestedStateNodes_.erase(this->requestedStateNodes_.begin());
    177230            }
    178231
    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();
     232            // UPDATE, Core first
     233            try
     234            {
     235                this->core_->update(*this->gameClock_);
     236            }
     237            catch (...)
     238            {
     239                COUT(0) << "An exception occured while ticking the Core. This should really never happen!" << std::endl;
     240                COUT(0) << "Closing the program." << std::endl;
     241                this->stop();
     242                break;
     243            }
     244
     245            // UPDATE, GameStates bottom to top in the stack
     246            // Note: The first element is the empty root state, which doesn't need ticking
     247            for (std::vector<GameState*>::const_iterator it = this->activeStates_.begin() + 1;
    182248                it != this->activeStates_.end(); ++it)
    183249            {
    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));
     250                bool threwException = false;
     251                try
     252                {
     253                    // Add tick time for most of the states
     254                    uint64_t timeBeforeTick;
     255                    if (!(*it)->ignoreTickTime())
     256                        timeBeforeTick = this->gameClock_->getRealMicroseconds();
     257                    (*it)->update(*this->gameClock_);
     258                    if (!(*it)->ignoreTickTime())
     259                        this->addTickTime(static_cast<uint32_t>(this->gameClock_->getRealMicroseconds() - timeBeforeTick));
     260                }
     261                catch (const std::exception& ex)
     262                {
     263                    threwException = true;
     264                    COUT(0) << "Exception while ticking: " << ex.what() << std::endl;
     265                }
     266                catch (...)
     267                {
     268                    threwException = true;
     269                }
     270                if (threwException)
     271                {
     272                    COUT(1) << "An exception occured while ticking GameState '" << (*it)->getName() << "'. This should really never happen!" << std::endl;
     273                    COUT(1) << "Unloading all GameStates depending on the one that crashed." << std::endl;
     274                    if ((*it)->getParent() != NULL)
     275                        this->requestState((*it)->getParent()->getName());
     276                    else
     277                        this->stop();
     278                    break;
     279                }
     280
    193281            }
    194282
    195283            // STATISTICS
    196             if (this->periodTime_ > statisticsRefreshCycle_)
    197             {
    198                 std::list<statisticsTickInfo>::iterator it = this->statisticsTickTimes_.begin();
     284            if (this->periodTime_ > this->configuration_->statisticsRefreshCycle_)
     285            {
     286                std::list<StatisticsTickInfo>::iterator it = this->statisticsTickTimes_.begin();
    199287                assert(it != this->statisticsTickTimes_.end());
    200                 int64_t lastTime = currentTime - this->statisticsAvgLength_;
     288                int64_t lastTime = currentTime - this->configuration_->statisticsAvgLength_;
    201289                if ((int64_t)it->tickTime < lastTime)
    202290                {
    203291                    do
    204292                    {
    205                         assert(this->periodTickTime_ > it->tickLength);
     293                        assert(this->periodTickTime_ >= it->tickLength);
    206294                        this->periodTickTime_ -= it->tickLength;
    207295                        ++it;
     
    215303                this->avgTickTime_ = static_cast<float>(this->periodTickTime_) / framesPerPeriod / 1000.0f;
    216304
    217                 this->periodTime_ -= this->statisticsRefreshCycle_;
     305                this->periodTime_ -= this->configuration_->statisticsRefreshCycle_;
    218306            }
    219307        }
    220308
    221309        // UNLOAD all remaining states
    222         while (!this->activeStates_.empty())
     310        while (this->activeStates_.size() > 1)
    223311            this->unloadState(this->activeStates_.back());
    224         this->activeStateNode_.reset();
     312        this->activeStateNode_ = this->rootStateNode_;
    225313        this->requestedStateNodes_.clear();
    226314    }
     
    228316    void Game::stop()
    229317    {
    230         this->abort_ = true;
     318        this->bAbort_ = true;
    231319    }
    232320
     
    244332    {
    245333        GameState* state = this->getState(name);
    246         if (state == NULL || this->activeStateNode_ == NULL)
     334        if (state == NULL)
    247335            return;
    248336
    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?
     337        //if (this->bChangingState_)
     338        //{
     339        //    COUT(2) << "Warning: Requesting GameStates while loading/unloading a GameState is illegal! Ignoring." << std::endl;
     340        //    return;
     341        //}
     342
     343        shared_ptr<GameStateTreeNode> lastRequestedNode;
     344        if (this->requestedStateNodes_.empty())
     345            lastRequestedNode = this->activeStateNode_;
     346        else
     347            lastRequestedNode = this->requestedStateNodes_.back();
    255348        if (state == lastRequestedNode->state_)
    256349        {
     
    260353
    261354        // Check children first
     355        std::vector<shared_ptr<GameStateTreeNode> > requestedNodes;
    262356        for (unsigned int i = 0; i < lastRequestedNode->children_.size(); ++i)
    263357        {
    264358            if (lastRequestedNode->children_[i]->state_ == state)
    265359            {
    266                 requestedNode = lastRequestedNode->children_[i];
     360                requestedNodes.push_back(lastRequestedNode->children_[i]);
    267361                break;
    268362            }
    269363        }
    270364
    271         // Check parent and all its grand parents
    272         shared_ptr<GameStateTreeNode> currentNode = lastRequestedNode;
    273         while (requestedNode == NULL && currentNode != NULL)
    274         {
    275             if (currentNode->state_ == state)
    276                 requestedNode = currentNode;
    277             currentNode = currentNode->parent_.lock();
    278         }
    279 
    280         if (requestedNode == NULL)
     365        if (requestedNodes.empty())
     366        {
     367            // Check parent and all its grand parents
     368            shared_ptr<GameStateTreeNode> currentNode = lastRequestedNode;
     369            while (currentNode != NULL)
     370            {
     371                if (currentNode->state_ == state)
     372                    break;
     373                currentNode = currentNode->parent_.lock();
     374                requestedNodes.push_back(currentNode);
     375            }
     376        }
     377
     378        if (requestedNodes.empty())
    281379            COUT(1) << "Error: Requested GameState transition is not allowed. Ignoring." << std::endl;
    282380        else
    283             this->requestedStateNodes_.push_back(requestedNode);
     381            this->requestedStateNodes_.insert(requestedStateNodes_.end(), requestedNodes.begin(), requestedNodes.end());
    284382    }
    285383
     
    293391    void Game::popState()
    294392    {
    295         if (this->activeStateNode_ != NULL && this->requestedStateNodes_.back()->parent_.lock())
    296             this->requestState(this->requestedStateNodes_.back()->parent_.lock()->state_->getName());
     393        shared_ptr<GameStateTreeNode> lastRequestedNode;
     394        if (this->requestedStateNodes_.empty())
     395            lastRequestedNode = this->activeStateNode_;
    297396        else
    298             COUT(2) << "Warning: Could not pop GameState. Ignoring." << std::endl;
     397            lastRequestedNode = this->requestedStateNodes_.back();
     398        if (lastRequestedNode != this->rootStateNode_)
     399            this->requestState(lastRequestedNode->parent_.lock()->state_->getName());
     400        else
     401            COUT(2) << "Warning: Can't pop the internal dummy root GameState" << std::endl;
    299402    }
    300403
    301404    GameState* Game::getState(const std::string& name)
    302405    {
    303         std::map<std::string, GameState*>::const_iterator it = allStates_s.find(getLowercase(name));
    304         if (it != allStates_s.end())
     406        std::map<std::string, GameState*>::const_iterator it = gameStates_.find(getLowercase(name));
     407        if (it != gameStates_.end())
    305408            return it->second;
    306409        else
     
    325428            while(pos < str.size() && str[pos] != ' ')
    326429                ++pos;
    327             stateStrings.push_back(std::pair<std::string, unsigned>(
    328                 str.substr(startPos, pos - startPos), indentation));
     430            stateStrings.push_back(std::make_pair(str.substr(startPos, pos - startPos), indentation));
    329431        }
    330432        unsigned int currentLevel = 0;
    331         shared_ptr<GameStateTreeNode> currentNode;
     433        shared_ptr<GameStateTreeNode> currentNode = this->rootStateNode_;
    332434        for (std::vector<std::pair<std::string, unsigned> >::const_iterator it = stateStrings.begin(); it != stateStrings.end(); ++it)
    333435        {
    334436            std::string newStateName = it->first;
    335             unsigned newLevel = it->second;
     437            unsigned newLevel = it->second + 1; // empty root is 0
    336438            GameState* newState = this->getState(newStateName);
    337439            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;
     440                ThrowException(GameState, "GameState with name '" << newStateName << "' not found!");
     441            if (newState == this->rootStateNode_->state_)
     442                ThrowException(GameState, "You shouldn't use 'emptyRootGameState' in the hierarchy...");
     443            shared_ptr<GameStateTreeNode> newNode(new GameStateTreeNode);
     444            newNode->state_ = newState;
     445
     446            if (newLevel <= currentLevel)
     447            {
     448                do
     449                    currentNode = currentNode->parent_.lock();
     450                while (newLevel <= --currentLevel);
     451            }
     452            if (newLevel == currentLevel + 1)
     453            {
     454                // Add the child
     455                newNode->parent_ = currentNode;
     456                currentNode->children_.push_back(newNode);
     457                currentNode->state_->addChild(newNode->state_);
    376458            }
    377459            else
    378             {
    379                 ThrowException(GameState, "No root GameState specified!");
    380             }
     460                ThrowException(GameState, "Indentation error while parsing the hierarchy.");
     461            currentNode = newNode;
     462            currentLevel = newLevel;
    381463        }
    382464    }
     
    386468    void Game::loadState(GameState* state)
    387469    {
     470        this->bChangingState_ = true;
     471        state->activate();
    388472        if (!this->activeStates_.empty())
    389473            this->activeStates_.back()->activity_.topState = false;
    390         state->activate();
     474        this->activeStates_.push_back(state);
    391475        state->activity_.topState = true;
    392         this->activeStates_.push_back(state);
     476        this->bChangingState_ = false;
    393477    }
    394478
    395479    void Game::unloadState(orxonox::GameState* state)
    396480    {
     481        this->bChangingState_ = true;
    397482        state->activity_.topState = false;
    398         state->deactivate();
    399483        this->activeStates_.pop_back();
    400484        if (!this->activeStates_.empty())
    401485            this->activeStates_.back()->activity_.topState = true;
    402     }
    403 
    404     /*static*/ bool Game::addGameState(GameState* state)
    405     {
    406         std::map<std::string, GameState*>::const_iterator it = allStates_s.find(getLowercase(state->getName()));
    407         if (it == allStates_s.end())
    408             allStates_s[getLowercase(state->getName())] = state;
    409         else
    410             ThrowException(GameState, "Cannot add two GameStates with the same name to 'Game'.");
    411 
    412         // just a required dummy return value
    413         return true;
    414     }
    415 
    416     /*static*/ void Game::destroyStates()
    417     {
    418         // Delete all GameStates created by the macros
    419         for (std::map<std::string, GameState*>::const_iterator it = allStates_s.begin(); it != allStates_s.end(); ++it)
     486        try
     487        {
     488            state->deactivate();
     489        }
     490        catch (const std::exception& ex)
     491        {
     492            COUT(2) << "Warning: Unloading GameState '" << state->getName() << "' threw an exception: " << ex.what() << std::endl;
     493            COUT(2) << "         There might be potential resource leaks involved! To avoid this, improve exception-safety." << std::endl;
     494        }
     495        this->bChangingState_ = false;
     496    }
     497
     498    std::map<std::string, Game::GameStateFactory*> Game::GameStateFactory::factories_s;
     499
     500    /*static*/ GameState* Game::GameStateFactory::fabricate(const std::string& className, const GameStateConstrParams& params)
     501    {
     502        std::map<std::string, GameStateFactory*>::const_iterator it = factories_s.find(className);
     503        assert(it != factories_s.end());
     504        return it->second->fabricate(params);
     505    }
     506
     507    /*static*/ void Game::GameStateFactory::destroyFactories()
     508    {
     509        for (std::map<std::string, GameStateFactory*>::const_iterator it = factories_s.begin(); it != factories_s.end(); ++it)
    420510            delete it->second;
    421         allStates_s.clear();
     511        factories_s.clear();
    422512    }
    423513}
Note: See TracChangeset for help on using the changeset viewer.