Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

Ignore:
Timestamp:
Sep 12, 2010, 12:47:30 AM (14 years ago)
Author:
rgrieder
Message:

Basic stuff up and running for the Qt sandbox.
No GUI support yet.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • sandbox_qt/src/libraries/core/Game.cc

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