Changeset 3280 for code/trunk/src/core/Game.cc
- Timestamp:
- Jul 12, 2009, 11:58:01 PM (16 years ago)
- Location:
- code/trunk
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
code/trunk
- Property svn:mergeinfo changed
/code/branches/core4 (added) merged: 3235-3237,3245-3250,3253-3254,3260-3261,3265,3270
- Property svn:mergeinfo changed
-
code/trunk/src/core/Game.cc
r3196 r3280 58 58 SetConsoleCommandShortcutExternAlias(stop_game, "exit"); 59 59 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 61 69 { 62 70 GameState* state_; … … 65 73 }; 66 74 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 69 103 70 104 /** … … 74 108 Game::Game(int argc, char** argv) 75 109 { 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 } 77 115 singletonRef_s = this; 78 116 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); 80 122 81 123 // reset statistics … … 87 129 this->avgTickTime_ = 0.0f; 88 130 89 90 131 // Set up a basic clock to keep time 91 132 this->gameClock_ = new Clock(); 92 133 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(); 98 155 } 99 156 … … 103 160 Game::~Game() 104 161 { 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 106 171 delete this->core_; 107 108 172 delete this->gameClock_; 109 173 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 137 178 } 138 179 … … 147 188 void Game::run() 148 189 { 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; 153 192 154 193 // START GAME 155 194 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)) 157 196 { 158 197 this->gameClock_->capture(); … … 160 199 161 200 // STATISTICS 162 statisticsTickInfo tickInfo = {currentTime, 0};201 StatisticsTickInfo tickInfo = {currentTime, 0}; 163 202 statisticsTickTimes_.push_back(tickInfo); 164 203 this->periodTime_ += this->gameClock_->getDeltaTimeMicroseconds(); 165 204 166 205 // UPDATE STATE STACK 167 while (this->requestedStateNodes_.size() > 1)168 { 169 // Note: this->requestedStateNodes_.front() is the currently active state node170 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()) 172 211 this->unloadState(this->activeStateNode_->state_); 173 212 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; 176 229 this->requestedStateNodes_.erase(this->requestedStateNodes_.begin()); 177 230 } 178 231 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; 182 248 it != this->activeStates_.end(); ++it) 183 249 { 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 193 281 } 194 282 195 283 // 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(); 199 287 assert(it != this->statisticsTickTimes_.end()); 200 int64_t lastTime = currentTime - this-> statisticsAvgLength_;288 int64_t lastTime = currentTime - this->configuration_->statisticsAvgLength_; 201 289 if ((int64_t)it->tickTime < lastTime) 202 290 { 203 291 do 204 292 { 205 assert(this->periodTickTime_ > it->tickLength);293 assert(this->periodTickTime_ >= it->tickLength); 206 294 this->periodTickTime_ -= it->tickLength; 207 295 ++it; … … 215 303 this->avgTickTime_ = static_cast<float>(this->periodTickTime_) / framesPerPeriod / 1000.0f; 216 304 217 this->periodTime_ -= this-> statisticsRefreshCycle_;305 this->periodTime_ -= this->configuration_->statisticsRefreshCycle_; 218 306 } 219 307 } 220 308 221 309 // UNLOAD all remaining states 222 while ( !this->activeStates_.empty())310 while (this->activeStates_.size() > 1) 223 311 this->unloadState(this->activeStates_.back()); 224 this->activeStateNode_ .reset();312 this->activeStateNode_ = this->rootStateNode_; 225 313 this->requestedStateNodes_.clear(); 226 314 } … … 228 316 void Game::stop() 229 317 { 230 this-> abort_ = true;318 this->bAbort_ = true; 231 319 } 232 320 … … 244 332 { 245 333 GameState* state = this->getState(name); 246 if (state == NULL || this->activeStateNode_ == NULL)334 if (state == NULL) 247 335 return; 248 336 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(); 255 348 if (state == lastRequestedNode->state_) 256 349 { … … 260 353 261 354 // Check children first 355 std::vector<shared_ptr<GameStateTreeNode> > requestedNodes; 262 356 for (unsigned int i = 0; i < lastRequestedNode->children_.size(); ++i) 263 357 { 264 358 if (lastRequestedNode->children_[i]->state_ == state) 265 359 { 266 requestedNode = lastRequestedNode->children_[i];360 requestedNodes.push_back(lastRequestedNode->children_[i]); 267 361 break; 268 362 } 269 363 } 270 364 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()) 281 379 COUT(1) << "Error: Requested GameState transition is not allowed. Ignoring." << std::endl; 282 380 else 283 this->requestedStateNodes_. push_back(requestedNode);381 this->requestedStateNodes_.insert(requestedStateNodes_.end(), requestedNodes.begin(), requestedNodes.end()); 284 382 } 285 383 … … 293 391 void Game::popState() 294 392 { 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_; 297 396 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; 299 402 } 300 403 301 404 GameState* Game::getState(const std::string& name) 302 405 { 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()) 305 408 return it->second; 306 409 else … … 325 428 while(pos < str.size() && str[pos] != ' ') 326 429 ++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)); 329 431 } 330 432 unsigned int currentLevel = 0; 331 shared_ptr<GameStateTreeNode> currentNode ;433 shared_ptr<GameStateTreeNode> currentNode = this->rootStateNode_; 332 434 for (std::vector<std::pair<std::string, unsigned> >::const_iterator it = stateStrings.begin(); it != stateStrings.end(); ++it) 333 435 { 334 436 std::string newStateName = it->first; 335 unsigned newLevel = it->second ;437 unsigned newLevel = it->second + 1; // empty root is 0 336 438 GameState* newState = this->getState(newStateName); 337 439 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_); 376 458 } 377 459 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; 381 463 } 382 464 } … … 386 468 void Game::loadState(GameState* state) 387 469 { 470 this->bChangingState_ = true; 471 state->activate(); 388 472 if (!this->activeStates_.empty()) 389 473 this->activeStates_.back()->activity_.topState = false; 390 state->activate();474 this->activeStates_.push_back(state); 391 475 state->activity_.topState = true; 392 this-> activeStates_.push_back(state);476 this->bChangingState_ = false; 393 477 } 394 478 395 479 void Game::unloadState(orxonox::GameState* state) 396 480 { 481 this->bChangingState_ = true; 397 482 state->activity_.topState = false; 398 state->deactivate();399 483 this->activeStates_.pop_back(); 400 484 if (!this->activeStates_.empty()) 401 485 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) 420 510 delete it->second; 421 allStates_s.clear();511 factories_s.clear(); 422 512 } 423 513 }
Note: See TracChangeset
for help on using the changeset viewer.