| [1505] | 1 | /* |
|---|
| 2 | * ORXONOX - the hottest 3D action shooter ever to exist |
|---|
| 3 | * > www.orxonox.net < |
|---|
| 4 | * |
|---|
| 5 | * |
|---|
| 6 | * License notice: |
|---|
| 7 | * |
|---|
| 8 | * This program is free software; you can redistribute it and/or |
|---|
| 9 | * modify it under the terms of the GNU General Public License |
|---|
| 10 | * as published by the Free Software Foundation; either version 2 |
|---|
| 11 | * of the License, or (at your option) any later version. |
|---|
| 12 | * |
|---|
| 13 | * This program is distributed in the hope that it will be useful, |
|---|
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 16 | * GNU General Public License for more details. |
|---|
| 17 | * |
|---|
| 18 | * You should have received a copy of the GNU General Public License |
|---|
| 19 | * along with this program; if not, write to the Free Software |
|---|
| 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|---|
| 21 | * |
|---|
| 22 | * Author: |
|---|
| 23 | * Fabian 'x3n' Landau |
|---|
| [2896] | 24 | * Reto Grieder |
|---|
| [1505] | 25 | * Co-authors: |
|---|
| [2896] | 26 | * ... |
|---|
| [1505] | 27 | * |
|---|
| 28 | */ |
|---|
| 29 | |
|---|
| 30 | /** |
|---|
| [3196] | 31 | @file |
|---|
| 32 | @brief |
|---|
| 33 | Implementation of the Core singleton with its global variables (avoids boost include) |
|---|
| [1505] | 34 | */ |
|---|
| 35 | |
|---|
| [1524] | 36 | #include "Core.h" |
|---|
| [2710] | 37 | |
|---|
| [1756] | 38 | #include <cassert> |
|---|
| [2710] | 39 | #include <fstream> |
|---|
| 40 | #include <cstdlib> |
|---|
| 41 | #include <cstdio> |
|---|
| [5693] | 42 | #include <boost/version.hpp> |
|---|
| [2710] | 43 | #include <boost/filesystem.hpp> |
|---|
| 44 | |
|---|
| 45 | #ifdef ORXONOX_PLATFORM_WINDOWS |
|---|
| [2896] | 46 | # ifndef WIN32_LEAN_AND_MEAN |
|---|
| 47 | # define WIN32_LEAN_AND_MEAN |
|---|
| 48 | # endif |
|---|
| [2710] | 49 | # include <windows.h> |
|---|
| [3214] | 50 | # undef min |
|---|
| 51 | # undef max |
|---|
| [2710] | 52 | #elif defined(ORXONOX_PLATFORM_APPLE) |
|---|
| 53 | # include <sys/param.h> |
|---|
| 54 | # include <mach-o/dyld.h> |
|---|
| 55 | #else /* Linux */ |
|---|
| 56 | # include <sys/types.h> |
|---|
| 57 | # include <unistd.h> |
|---|
| 58 | #endif |
|---|
| 59 | |
|---|
| 60 | #include "SpecialConfig.h" |
|---|
| [2896] | 61 | #include "util/Debug.h" |
|---|
| [2710] | 62 | #include "util/Exception.h" |
|---|
| [2896] | 63 | #include "util/SignalHandler.h" |
|---|
| 64 | #include "Clock.h" |
|---|
| 65 | #include "CommandLine.h" |
|---|
| 66 | #include "ConfigFileManager.h" |
|---|
| 67 | #include "ConfigValueIncludes.h" |
|---|
| 68 | #include "CoreIncludes.h" |
|---|
| [5693] | 69 | #include "DynLibManager.h" |
|---|
| [2896] | 70 | #include "Factory.h" |
|---|
| 71 | #include "Identifier.h" |
|---|
| [1505] | 72 | #include "Language.h" |
|---|
| [5695] | 73 | #include "LuaState.h" |
|---|
| [1505] | 74 | |
|---|
| [5693] | 75 | // Boost 1.36 has some issues with deprecated functions that have been omitted |
|---|
| 76 | #if (BOOST_VERSION == 103600) |
|---|
| 77 | # define BOOST_LEAF_FUNCTION filename |
|---|
| 78 | #else |
|---|
| 79 | # define BOOST_LEAF_FUNCTION leaf |
|---|
| 80 | #endif |
|---|
| 81 | |
|---|
| [1505] | 82 | namespace orxonox |
|---|
| 83 | { |
|---|
| [3196] | 84 | //! Static pointer to the singleton |
|---|
| [3370] | 85 | Core* Core::singletonPtr_s = 0; |
|---|
| [2662] | 86 | |
|---|
| [5695] | 87 | SetCommandLineArgument(externalDataPath, "").information("Path to the external data files"); |
|---|
| [3280] | 88 | SetCommandLineOnlyArgument(writingPathSuffix, "").information("Additional subfolder for config and log files"); |
|---|
| 89 | SetCommandLineArgument(settingsFile, "orxonox.ini").information("THE configuration file"); |
|---|
| 90 | #ifdef ORXONOX_PLATFORM_WINDOWS |
|---|
| 91 | SetCommandLineArgument(limitToCPU, 0).information("Limits the program to one cpu/core (1, 2, 3, etc.). 0 turns it off (default)"); |
|---|
| 92 | #endif |
|---|
| [2710] | 93 | |
|---|
| [3280] | 94 | /** |
|---|
| 95 | @brief |
|---|
| 96 | Helper class for the Core singleton: we cannot derive |
|---|
| 97 | Core from OrxonoxClass because we need to handle the Identifier |
|---|
| 98 | destruction in the Core destructor. |
|---|
| 99 | */ |
|---|
| 100 | class CoreConfiguration : public OrxonoxClass |
|---|
| [1505] | 101 | { |
|---|
| [3280] | 102 | public: |
|---|
| 103 | CoreConfiguration() |
|---|
| 104 | { |
|---|
| 105 | } |
|---|
| [2662] | 106 | |
|---|
| [3280] | 107 | void initialise() |
|---|
| 108 | { |
|---|
| 109 | RegisterRootObject(CoreConfiguration); |
|---|
| 110 | this->setConfigValues(); |
|---|
| 111 | } |
|---|
| 112 | |
|---|
| 113 | /** |
|---|
| 114 | @brief Function to collect the SetConfigValue-macro calls. |
|---|
| 115 | */ |
|---|
| 116 | void setConfigValues() |
|---|
| [2896] | 117 | { |
|---|
| [3280] | 118 | #ifdef NDEBUG |
|---|
| 119 | const unsigned int defaultLevelConsole = 1; |
|---|
| 120 | const unsigned int defaultLevelLogfile = 3; |
|---|
| 121 | const unsigned int defaultLevelShell = 1; |
|---|
| 122 | #else |
|---|
| 123 | const unsigned int defaultLevelConsole = 3; |
|---|
| 124 | const unsigned int defaultLevelLogfile = 4; |
|---|
| 125 | const unsigned int defaultLevelShell = 3; |
|---|
| 126 | #endif |
|---|
| 127 | SetConfigValue(softDebugLevelConsole_, defaultLevelConsole) |
|---|
| 128 | .description("The maximal level of debug output shown in the console") |
|---|
| 129 | .callback(this, &CoreConfiguration::debugLevelChanged); |
|---|
| 130 | SetConfigValue(softDebugLevelLogfile_, defaultLevelLogfile) |
|---|
| 131 | .description("The maximal level of debug output shown in the logfile") |
|---|
| 132 | .callback(this, &CoreConfiguration::debugLevelChanged); |
|---|
| 133 | SetConfigValue(softDebugLevelShell_, defaultLevelShell) |
|---|
| 134 | .description("The maximal level of debug output shown in the ingame shell") |
|---|
| 135 | .callback(this, &CoreConfiguration::debugLevelChanged); |
|---|
| 136 | |
|---|
| [3370] | 137 | SetConfigValue(language_, Language::getInstance().defaultLanguage_) |
|---|
| [3280] | 138 | .description("The language of the ingame text") |
|---|
| 139 | .callback(this, &CoreConfiguration::languageChanged); |
|---|
| 140 | SetConfigValue(bInitializeRandomNumberGenerator_, true) |
|---|
| 141 | .description("If true, all random actions are different each time you start the game") |
|---|
| 142 | .callback(this, &CoreConfiguration::initializeRandomNumberGenerator); |
|---|
| [2896] | 143 | } |
|---|
| [3280] | 144 | |
|---|
| 145 | /** |
|---|
| 146 | @brief Callback function if the debug level has changed. |
|---|
| 147 | */ |
|---|
| 148 | void debugLevelChanged() |
|---|
| [2896] | 149 | { |
|---|
| [3280] | 150 | // softDebugLevel_ is the maximum of the 3 variables |
|---|
| 151 | this->softDebugLevel_ = this->softDebugLevelConsole_; |
|---|
| 152 | if (this->softDebugLevelLogfile_ > this->softDebugLevel_) |
|---|
| 153 | this->softDebugLevel_ = this->softDebugLevelLogfile_; |
|---|
| 154 | if (this->softDebugLevelShell_ > this->softDebugLevel_) |
|---|
| 155 | this->softDebugLevel_ = this->softDebugLevelShell_; |
|---|
| 156 | |
|---|
| 157 | OutputHandler::setSoftDebugLevel(OutputHandler::LD_All, this->softDebugLevel_); |
|---|
| 158 | OutputHandler::setSoftDebugLevel(OutputHandler::LD_Console, this->softDebugLevelConsole_); |
|---|
| 159 | OutputHandler::setSoftDebugLevel(OutputHandler::LD_Logfile, this->softDebugLevelLogfile_); |
|---|
| 160 | OutputHandler::setSoftDebugLevel(OutputHandler::LD_Shell, this->softDebugLevelShell_); |
|---|
| [2896] | 161 | } |
|---|
| [2662] | 162 | |
|---|
| [3280] | 163 | /** |
|---|
| 164 | @brief Callback function if the language has changed. |
|---|
| 165 | */ |
|---|
| 166 | void languageChanged() |
|---|
| 167 | { |
|---|
| 168 | // Read the translation file after the language was configured |
|---|
| [3370] | 169 | Language::getInstance().readTranslatedLanguageFile(); |
|---|
| [3280] | 170 | } |
|---|
| [2896] | 171 | |
|---|
| [3280] | 172 | /** |
|---|
| 173 | @brief Sets the language in the config-file back to the default. |
|---|
| 174 | */ |
|---|
| 175 | void resetLanguage() |
|---|
| 176 | { |
|---|
| 177 | ResetConfigValue(language_); |
|---|
| 178 | } |
|---|
| 179 | |
|---|
| 180 | void initializeRandomNumberGenerator() |
|---|
| 181 | { |
|---|
| 182 | static bool bInitialized = false; |
|---|
| 183 | if (!bInitialized && this->bInitializeRandomNumberGenerator_) |
|---|
| 184 | { |
|---|
| 185 | srand(static_cast<unsigned int>(time(0))); |
|---|
| 186 | rand(); |
|---|
| 187 | bInitialized = true; |
|---|
| 188 | } |
|---|
| 189 | } |
|---|
| 190 | |
|---|
| 191 | int softDebugLevel_; //!< The debug level |
|---|
| 192 | int softDebugLevelConsole_; //!< The debug level for the console |
|---|
| 193 | int softDebugLevelLogfile_; //!< The debug level for the logfile |
|---|
| 194 | int softDebugLevelShell_; //!< The debug level for the ingame shell |
|---|
| 195 | std::string language_; //!< The language |
|---|
| 196 | bool bInitializeRandomNumberGenerator_; //!< If true, srand(time(0)) is called |
|---|
| 197 | |
|---|
| 198 | //! Path to the parent directory of the ones above if program was installed with relativ pahts |
|---|
| 199 | boost::filesystem::path rootPath_; |
|---|
| 200 | boost::filesystem::path executablePath_; //!< Path to the executable |
|---|
| [5693] | 201 | boost::filesystem::path modulePath_; //!< Path to the modules |
|---|
| [5695] | 202 | boost::filesystem::path dataPath_; //!< Path to the data file folder |
|---|
| [3280] | 203 | boost::filesystem::path configPath_; //!< Path to the config file folder |
|---|
| 204 | boost::filesystem::path logPath_; //!< Path to the log file folder |
|---|
| 205 | }; |
|---|
| 206 | |
|---|
| 207 | |
|---|
| [3323] | 208 | Core::Core(const std::string& cmdLine) |
|---|
| [3370] | 209 | // Cleanup guard for identifier destruction (incl. XMLPort, configValues, consoleCommands) |
|---|
| 210 | : identifierDestroyer_(Identifier::destroyAllIdentifiers) |
|---|
| 211 | , configuration_(new CoreConfiguration()) // Don't yet create config values! |
|---|
| 212 | , bDevRun_(false) |
|---|
| [3280] | 213 | { |
|---|
| [5693] | 214 | // Set the hard coded fixed paths |
|---|
| 215 | this->setFixedPaths(); |
|---|
| [3280] | 216 | |
|---|
| [5693] | 217 | // Create a new dynamic library manager |
|---|
| 218 | this->dynLibManager_.reset(new DynLibManager()); |
|---|
| [2896] | 219 | |
|---|
| [5693] | 220 | // Load modules |
|---|
| 221 | try |
|---|
| 222 | { |
|---|
| 223 | // We search for helper files with the following extension |
|---|
| [5695] | 224 | std::string moduleextension = specialConfig::moduleExtension; |
|---|
| [5693] | 225 | size_t moduleextensionlength = moduleextension.size(); |
|---|
| [2896] | 226 | |
|---|
| [5693] | 227 | // Search in the directory of our executable |
|---|
| 228 | boost::filesystem::path searchpath = this->configuration_->modulePath_; |
|---|
| [2896] | 229 | |
|---|
| [5693] | 230 | // Add that path to the PATH variable in case a module depends on another one |
|---|
| 231 | std::string pathVariable = getenv("PATH"); |
|---|
| 232 | putenv(const_cast<char*>(("PATH=" + pathVariable + ";" + configuration_->modulePath_.string()).c_str())); |
|---|
| 233 | |
|---|
| 234 | boost::filesystem::directory_iterator file(searchpath); |
|---|
| 235 | boost::filesystem::directory_iterator end; |
|---|
| 236 | |
|---|
| 237 | // Iterate through all files |
|---|
| 238 | while (file != end) |
|---|
| 239 | { |
|---|
| 240 | std::string filename = file->BOOST_LEAF_FUNCTION(); |
|---|
| 241 | |
|---|
| 242 | // Check if the file ends with the exension in question |
|---|
| 243 | if (filename.size() > moduleextensionlength) |
|---|
| 244 | { |
|---|
| 245 | if (filename.substr(filename.size() - moduleextensionlength) == moduleextension) |
|---|
| 246 | { |
|---|
| 247 | // We've found a helper file - now load the library with the same name |
|---|
| 248 | std::string library = filename.substr(0, filename.size() - moduleextensionlength); |
|---|
| 249 | boost::filesystem::path librarypath = searchpath / library; |
|---|
| 250 | |
|---|
| 251 | try |
|---|
| 252 | { |
|---|
| 253 | DynLibManager::getInstance().load(librarypath.string()); |
|---|
| 254 | } |
|---|
| 255 | catch (...) |
|---|
| 256 | { |
|---|
| [5747] | 257 | COUT(1) << "Couldn't load module \"" << librarypath.string() << "\": " << Exception::handleMessage() << std::endl; |
|---|
| [5693] | 258 | } |
|---|
| 259 | } |
|---|
| 260 | } |
|---|
| 261 | |
|---|
| 262 | ++file; |
|---|
| 263 | } |
|---|
| 264 | } |
|---|
| 265 | catch (...) |
|---|
| 266 | { |
|---|
| [5747] | 267 | COUT(1) << "An error occurred while loading modules: " << Exception::handleMessage() << std::endl; |
|---|
| [5693] | 268 | } |
|---|
| 269 | |
|---|
| 270 | // Parse command line arguments AFTER the modules have been loaded (static code!) |
|---|
| 271 | CommandLine::parseCommandLine(cmdLine); |
|---|
| 272 | |
|---|
| 273 | // Set configurable paths like log, config and media |
|---|
| 274 | this->setConfigurablePaths(); |
|---|
| 275 | |
|---|
| [2896] | 276 | // create a signal handler (only active for linux) |
|---|
| 277 | // This call is placed as soon as possible, but after the directories are set |
|---|
| [3370] | 278 | this->signalHandler_.reset(new SignalHandler()); |
|---|
| [3280] | 279 | this->signalHandler_->doCatch(configuration_->executablePath_.string(), Core::getLogPathString() + "orxonox_crash.log"); |
|---|
| [2896] | 280 | |
|---|
| [2710] | 281 | // Set the correct log path. Before this call, /tmp (Unix) or %TEMP% was used |
|---|
| 282 | OutputHandler::getOutStream().setLogPath(Core::getLogPathString()); |
|---|
| 283 | |
|---|
| [3280] | 284 | // Parse additional options file now that we know its path |
|---|
| 285 | CommandLine::parseFile(); |
|---|
| 286 | |
|---|
| 287 | #ifdef ORXONOX_PLATFORM_WINDOWS |
|---|
| 288 | // limit the main thread to the first core so that QueryPerformanceCounter doesn't jump |
|---|
| 289 | // do this after ogre has initialised. Somehow Ogre changes the settings again (not through |
|---|
| 290 | // the timer though). |
|---|
| 291 | int limitToCPU = CommandLine::getValue("limitToCPU"); |
|---|
| 292 | if (limitToCPU > 0) |
|---|
| 293 | setThreadAffinity(static_cast<unsigned int>(limitToCPU)); |
|---|
| 294 | #endif |
|---|
| 295 | |
|---|
| [2896] | 296 | // Manage ini files and set the default settings file (usually orxonox.ini) |
|---|
| [3370] | 297 | this->configFileManager_.reset(new ConfigFileManager()); |
|---|
| [2896] | 298 | this->configFileManager_->setFilename(ConfigFileType::Settings, |
|---|
| 299 | CommandLine::getValue("settingsFile").getString()); |
|---|
| 300 | |
|---|
| [3280] | 301 | // Required as well for the config values |
|---|
| [3370] | 302 | this->languageInstance_.reset(new Language()); |
|---|
| [2896] | 303 | |
|---|
| [5695] | 304 | // creates the class hierarchy for all classes with factories |
|---|
| 305 | Factory::createClassHierarchy(); |
|---|
| 306 | |
|---|
| [2896] | 307 | // Do this soon after the ConfigFileManager has been created to open up the |
|---|
| 308 | // possibility to configure everything below here |
|---|
| [3280] | 309 | this->configuration_->initialise(); |
|---|
| [1505] | 310 | } |
|---|
| 311 | |
|---|
| 312 | /** |
|---|
| [3370] | 313 | @brief |
|---|
| [5695] | 314 | All destruction code is handled by scoped_ptrs and ScopeGuards. |
|---|
| [1505] | 315 | */ |
|---|
| [1524] | 316 | Core::~Core() |
|---|
| [1505] | 317 | { |
|---|
| [3370] | 318 | } |
|---|
| [2896] | 319 | |
|---|
| [1747] | 320 | /** |
|---|
| [3280] | 321 | @brief Returns the softDebugLevel for the given device (returns a default-value if the class is right about to be created). |
|---|
| [1505] | 322 | @param device The device |
|---|
| 323 | @return The softDebugLevel |
|---|
| 324 | */ |
|---|
| [3280] | 325 | /*static*/ int Core::getSoftDebugLevel(OutputHandler::OutputDevice device) |
|---|
| [1505] | 326 | { |
|---|
| [2662] | 327 | switch (device) |
|---|
| [1505] | 328 | { |
|---|
| [2662] | 329 | case OutputHandler::LD_All: |
|---|
| [3280] | 330 | return Core::getInstance().configuration_->softDebugLevel_; |
|---|
| [2662] | 331 | case OutputHandler::LD_Console: |
|---|
| [3280] | 332 | return Core::getInstance().configuration_->softDebugLevelConsole_; |
|---|
| [2662] | 333 | case OutputHandler::LD_Logfile: |
|---|
| [3280] | 334 | return Core::getInstance().configuration_->softDebugLevelLogfile_; |
|---|
| [2662] | 335 | case OutputHandler::LD_Shell: |
|---|
| [3280] | 336 | return Core::getInstance().configuration_->softDebugLevelShell_; |
|---|
| [2662] | 337 | default: |
|---|
| 338 | assert(0); |
|---|
| 339 | return 2; |
|---|
| [1505] | 340 | } |
|---|
| 341 | } |
|---|
| 342 | |
|---|
| 343 | /** |
|---|
| 344 | @brief Sets the softDebugLevel for the given device. Please use this only temporary and restore the value afterwards, as it overrides the configured value. |
|---|
| 345 | @param device The device |
|---|
| 346 | @param level The level |
|---|
| 347 | */ |
|---|
| [3280] | 348 | /*static*/ void Core::setSoftDebugLevel(OutputHandler::OutputDevice device, int level) |
|---|
| 349 | { |
|---|
| [2662] | 350 | if (device == OutputHandler::LD_All) |
|---|
| [3280] | 351 | Core::getInstance().configuration_->softDebugLevel_ = level; |
|---|
| [2662] | 352 | else if (device == OutputHandler::LD_Console) |
|---|
| [3280] | 353 | Core::getInstance().configuration_->softDebugLevelConsole_ = level; |
|---|
| [2662] | 354 | else if (device == OutputHandler::LD_Logfile) |
|---|
| [3280] | 355 | Core::getInstance().configuration_->softDebugLevelLogfile_ = level; |
|---|
| [2662] | 356 | else if (device == OutputHandler::LD_Shell) |
|---|
| [3280] | 357 | Core::getInstance().configuration_->softDebugLevelShell_ = level; |
|---|
| [1747] | 358 | |
|---|
| [2662] | 359 | OutputHandler::setSoftDebugLevel(device, level); |
|---|
| [3280] | 360 | } |
|---|
| [1505] | 361 | |
|---|
| 362 | /** |
|---|
| 363 | @brief Returns the configured language. |
|---|
| 364 | */ |
|---|
| [3280] | 365 | /*static*/ const std::string& Core::getLanguage() |
|---|
| [1505] | 366 | { |
|---|
| [3280] | 367 | return Core::getInstance().configuration_->language_; |
|---|
| [1505] | 368 | } |
|---|
| 369 | |
|---|
| 370 | /** |
|---|
| 371 | @brief Sets the language in the config-file back to the default. |
|---|
| 372 | */ |
|---|
| [3280] | 373 | /*static*/ void Core::resetLanguage() |
|---|
| [1505] | 374 | { |
|---|
| [3280] | 375 | Core::getInstance().configuration_->resetLanguage(); |
|---|
| [1505] | 376 | } |
|---|
| 377 | |
|---|
| [5695] | 378 | /*static*/ const boost::filesystem::path& Core::getDataPath() |
|---|
| [2710] | 379 | { |
|---|
| [5695] | 380 | return getInstance().configuration_->dataPath_; |
|---|
| [2710] | 381 | } |
|---|
| [5695] | 382 | /*static*/ std::string Core::getDataPathString() |
|---|
| [2710] | 383 | { |
|---|
| [5695] | 384 | return getInstance().configuration_->dataPath_.string() + '/'; |
|---|
| [2710] | 385 | } |
|---|
| 386 | |
|---|
| 387 | /*static*/ const boost::filesystem::path& Core::getConfigPath() |
|---|
| 388 | { |
|---|
| [3280] | 389 | return getInstance().configuration_->configPath_; |
|---|
| [2710] | 390 | } |
|---|
| 391 | /*static*/ std::string Core::getConfigPathString() |
|---|
| 392 | { |
|---|
| [3280] | 393 | return getInstance().configuration_->configPath_.string() + '/'; |
|---|
| [2710] | 394 | } |
|---|
| 395 | |
|---|
| 396 | /*static*/ const boost::filesystem::path& Core::getLogPath() |
|---|
| 397 | { |
|---|
| [3280] | 398 | return getInstance().configuration_->logPath_; |
|---|
| [2710] | 399 | } |
|---|
| 400 | /*static*/ std::string Core::getLogPathString() |
|---|
| 401 | { |
|---|
| [3280] | 402 | return getInstance().configuration_->logPath_.string() + '/'; |
|---|
| [2710] | 403 | } |
|---|
| 404 | |
|---|
| [3370] | 405 | /*static*/ const boost::filesystem::path& Core::getRootPath() |
|---|
| 406 | { |
|---|
| 407 | return getInstance().configuration_->rootPath_; |
|---|
| 408 | } |
|---|
| 409 | /*static*/ std::string Core::getRootPathString() |
|---|
| 410 | { |
|---|
| 411 | return getInstance().configuration_->rootPath_.string() + '/'; |
|---|
| 412 | } |
|---|
| 413 | |
|---|
| [2710] | 414 | /** |
|---|
| [2896] | 415 | @note |
|---|
| 416 | The code of this function has been copied and adjusted from OGRE, an open source graphics engine. |
|---|
| 417 | (Object-oriented Graphics Rendering Engine) |
|---|
| 418 | For the latest info, see http://www.ogre3d.org/ |
|---|
| 419 | |
|---|
| 420 | Copyright (c) 2000-2008 Torus Knot Software Ltd |
|---|
| 421 | |
|---|
| 422 | OGRE is licensed under the LGPL. For more info, see OGRE license. |
|---|
| [2710] | 423 | */ |
|---|
| [2896] | 424 | void Core::setThreadAffinity(int limitToCPU) |
|---|
| [2710] | 425 | { |
|---|
| [3280] | 426 | #ifdef ORXONOX_PLATFORM_WINDOWS |
|---|
| 427 | |
|---|
| [2896] | 428 | if (limitToCPU <= 0) |
|---|
| 429 | return; |
|---|
| [2710] | 430 | |
|---|
| [2896] | 431 | unsigned int coreNr = limitToCPU - 1; |
|---|
| 432 | // Get the current process core mask |
|---|
| 433 | DWORD procMask; |
|---|
| 434 | DWORD sysMask; |
|---|
| 435 | # if _MSC_VER >= 1400 && defined (_M_X64) |
|---|
| 436 | GetProcessAffinityMask(GetCurrentProcess(), (PDWORD_PTR)&procMask, (PDWORD_PTR)&sysMask); |
|---|
| 437 | # else |
|---|
| 438 | GetProcessAffinityMask(GetCurrentProcess(), &procMask, &sysMask); |
|---|
| 439 | # endif |
|---|
| [2710] | 440 | |
|---|
| [2896] | 441 | // If procMask is 0, consider there is only one core available |
|---|
| 442 | // (using 0 as procMask will cause an infinite loop below) |
|---|
| 443 | if (procMask == 0) |
|---|
| 444 | procMask = 1; |
|---|
| 445 | |
|---|
| 446 | // if the core specified with coreNr is not available, take the lowest one |
|---|
| 447 | if (!(procMask & (1 << coreNr))) |
|---|
| 448 | coreNr = 0; |
|---|
| 449 | |
|---|
| 450 | // Find the lowest core that this process uses and coreNr suggests |
|---|
| 451 | DWORD threadMask = 1; |
|---|
| 452 | while ((threadMask & procMask) == 0 || (threadMask < (1u << coreNr))) |
|---|
| 453 | threadMask <<= 1; |
|---|
| 454 | |
|---|
| 455 | // Set affinity to the first core |
|---|
| 456 | SetThreadAffinityMask(GetCurrentThread(), threadMask); |
|---|
| 457 | #endif |
|---|
| [2710] | 458 | } |
|---|
| 459 | |
|---|
| 460 | /** |
|---|
| 461 | @brief |
|---|
| [5693] | 462 | Retrievs the executable path and sets all hard coded fixed path (currently only the module path) |
|---|
| 463 | Also checks for "orxonox_dev_build.keep_me" in the executable diretory. |
|---|
| 464 | If found it means that this is not an installed run, hence we |
|---|
| 465 | don't write the logs and config files to ~/.orxonox |
|---|
| 466 | @throw |
|---|
| 467 | GeneralException |
|---|
| [2710] | 468 | */ |
|---|
| [5693] | 469 | void Core::setFixedPaths() |
|---|
| [2710] | 470 | { |
|---|
| [5693] | 471 | ////////////////////////// |
|---|
| 472 | // FIND EXECUTABLE PATH // |
|---|
| 473 | ////////////////////////// |
|---|
| 474 | |
|---|
| [2710] | 475 | #ifdef ORXONOX_PLATFORM_WINDOWS |
|---|
| 476 | // get executable module |
|---|
| 477 | TCHAR buffer[1024]; |
|---|
| 478 | if (GetModuleFileName(NULL, buffer, 1024) == 0) |
|---|
| 479 | ThrowException(General, "Could not retrieve executable path."); |
|---|
| 480 | |
|---|
| 481 | #elif defined(ORXONOX_PLATFORM_APPLE) |
|---|
| 482 | char buffer[1024]; |
|---|
| 483 | unsigned long path_len = 1023; |
|---|
| 484 | if (_NSGetExecutablePath(buffer, &path_len)) |
|---|
| 485 | ThrowException(General, "Could not retrieve executable path."); |
|---|
| 486 | |
|---|
| 487 | #else /* Linux */ |
|---|
| 488 | /* written by Nicolai Haehnle <prefect_@gmx.net> */ |
|---|
| 489 | |
|---|
| 490 | /* Get our PID and build the name of the link in /proc */ |
|---|
| 491 | char linkname[64]; /* /proc/<pid>/exe */ |
|---|
| 492 | if (snprintf(linkname, sizeof(linkname), "/proc/%i/exe", getpid()) < 0) |
|---|
| 493 | { |
|---|
| 494 | /* This should only happen on large word systems. I'm not sure |
|---|
| 495 | what the proper response is here. |
|---|
| 496 | Since it really is an assert-like condition, aborting the |
|---|
| 497 | program seems to be in order. */ |
|---|
| 498 | assert(false); |
|---|
| 499 | } |
|---|
| 500 | |
|---|
| 501 | /* Now read the symbolic link */ |
|---|
| 502 | char buffer[1024]; |
|---|
| 503 | int ret; |
|---|
| 504 | ret = readlink(linkname, buffer, 1024); |
|---|
| 505 | /* In case of an error, leave the handling up to the caller */ |
|---|
| 506 | if (ret == -1) |
|---|
| 507 | ThrowException(General, "Could not retrieve executable path."); |
|---|
| 508 | |
|---|
| 509 | /* Ensure proper NUL termination */ |
|---|
| 510 | buffer[ret] = 0; |
|---|
| 511 | #endif |
|---|
| 512 | |
|---|
| [3280] | 513 | configuration_->executablePath_ = boost::filesystem::path(buffer); |
|---|
| [2710] | 514 | #ifndef ORXONOX_PLATFORM_APPLE |
|---|
| [3280] | 515 | configuration_->executablePath_ = configuration_->executablePath_.branch_path(); // remove executable name |
|---|
| [2710] | 516 | #endif |
|---|
| 517 | |
|---|
| [5693] | 518 | ///////////////////// |
|---|
| 519 | // SET MODULE PATH // |
|---|
| 520 | ///////////////////// |
|---|
| 521 | |
|---|
| [3280] | 522 | if (boost::filesystem::exists(configuration_->executablePath_ / "orxonox_dev_build.keep_me")) |
|---|
| [2710] | 523 | { |
|---|
| 524 | COUT(1) << "Running from the build tree." << std::endl; |
|---|
| [3370] | 525 | Core::bDevRun_ = true; |
|---|
| [5695] | 526 | configuration_->modulePath_ = specialConfig::moduleDevDirectory; |
|---|
| [2710] | 527 | } |
|---|
| 528 | else |
|---|
| 529 | { |
|---|
| [5693] | 530 | |
|---|
| [2710] | 531 | #ifdef INSTALL_COPYABLE // --> relative paths |
|---|
| [5693] | 532 | |
|---|
| [2710] | 533 | // Also set the root path |
|---|
| [5695] | 534 | boost::filesystem::path relativeExecutablePath(specialConfig::defaultRuntimePath); |
|---|
| [3280] | 535 | configuration_->rootPath_ = configuration_->executablePath_; |
|---|
| 536 | while (!boost::filesystem::equivalent(configuration_->rootPath_ / relativeExecutablePath, configuration_->executablePath_) |
|---|
| 537 | && !configuration_->rootPath_.empty()) |
|---|
| 538 | configuration_->rootPath_ = configuration_->rootPath_.branch_path(); |
|---|
| 539 | if (configuration_->rootPath_.empty()) |
|---|
| [2710] | 540 | ThrowException(General, "Could not derive a root directory. Might the binary installation directory contain '..' when taken relative to the installation prefix path?"); |
|---|
| 541 | |
|---|
| [5693] | 542 | // Module path is fixed as well |
|---|
| [5695] | 543 | configuration_->modulePath_ = configuration_->rootPath_ / specialConfig::defaultModulePath; |
|---|
| [5693] | 544 | |
|---|
| 545 | #else |
|---|
| 546 | |
|---|
| 547 | // There is no root path, so don't set it at all |
|---|
| 548 | // Module path is fixed as well |
|---|
| [5695] | 549 | configuration_->modulePath_ = specialConfig::moduleInstallDirectory; |
|---|
| [5693] | 550 | |
|---|
| 551 | #endif |
|---|
| 552 | } |
|---|
| 553 | } |
|---|
| 554 | |
|---|
| 555 | /** |
|---|
| 556 | @brief |
|---|
| 557 | Sets config, log and media path and creates folders if necessary. |
|---|
| 558 | @throws |
|---|
| 559 | GeneralException |
|---|
| 560 | */ |
|---|
| 561 | void Core::setConfigurablePaths() |
|---|
| 562 | { |
|---|
| 563 | if (Core::isDevelopmentRun()) |
|---|
| 564 | { |
|---|
| [5695] | 565 | configuration_->dataPath_ = specialConfig::dataDevDirectory; |
|---|
| 566 | configuration_->configPath_ = specialConfig::configDevDirectory; |
|---|
| 567 | configuration_->logPath_ = specialConfig::logDevDirectory; |
|---|
| [5693] | 568 | } |
|---|
| 569 | else |
|---|
| 570 | { |
|---|
| 571 | |
|---|
| 572 | #ifdef INSTALL_COPYABLE // --> relative paths |
|---|
| 573 | |
|---|
| [2710] | 574 | // Using paths relative to the install prefix, complete them |
|---|
| [5695] | 575 | configuration_->dataPath_ = configuration_->rootPath_ / specialConfig::defaultDataPath; |
|---|
| 576 | configuration_->configPath_ = configuration_->rootPath_ / specialConfig::defaultConfigPath; |
|---|
| 577 | configuration_->logPath_ = configuration_->rootPath_ / specialConfig::defaultLogPath; |
|---|
| [5693] | 578 | |
|---|
| [2710] | 579 | #else |
|---|
| 580 | |
|---|
| [5695] | 581 | configuration_->dataPath_ = specialConfig::dataInstallDirectory; |
|---|
| [2710] | 582 | |
|---|
| 583 | // Get user directory |
|---|
| 584 | # ifdef ORXONOX_PLATFORM_UNIX /* Apple? */ |
|---|
| 585 | char* userDataPathPtr(getenv("HOME")); |
|---|
| 586 | # else |
|---|
| 587 | char* userDataPathPtr(getenv("APPDATA")); |
|---|
| 588 | # endif |
|---|
| 589 | if (userDataPathPtr == NULL) |
|---|
| 590 | ThrowException(General, "Could not retrieve user data path."); |
|---|
| 591 | boost::filesystem::path userDataPath(userDataPathPtr); |
|---|
| 592 | userDataPath /= ".orxonox"; |
|---|
| 593 | |
|---|
| [5695] | 594 | configuration_->configPath_ = userDataPath / specialConfig::defaultConfigPath; |
|---|
| 595 | configuration_->logPath_ = userDataPath / specialConfig::defaultLogPath; |
|---|
| [5693] | 596 | |
|---|
| [2710] | 597 | #endif |
|---|
| [5693] | 598 | |
|---|
| [2710] | 599 | } |
|---|
| 600 | |
|---|
| 601 | // Option to put all the config and log files in a separate folder |
|---|
| [2896] | 602 | if (!CommandLine::getArgument("writingPathSuffix")->hasDefaultValue()) |
|---|
| [2710] | 603 | { |
|---|
| [2896] | 604 | std::string directory(CommandLine::getValue("writingPathSuffix").getString()); |
|---|
| [3280] | 605 | configuration_->configPath_ = configuration_->configPath_ / directory; |
|---|
| 606 | configuration_->logPath_ = configuration_->logPath_ / directory; |
|---|
| [2710] | 607 | } |
|---|
| 608 | |
|---|
| [5693] | 609 | // Create directories to avoid problems when opening files in non existent folders. |
|---|
| [2710] | 610 | std::vector<std::pair<boost::filesystem::path, std::string> > directories; |
|---|
| [3280] | 611 | directories.push_back(std::make_pair(boost::filesystem::path(configuration_->configPath_), "config")); |
|---|
| 612 | directories.push_back(std::make_pair(boost::filesystem::path(configuration_->logPath_), "log")); |
|---|
| [2710] | 613 | |
|---|
| 614 | for (std::vector<std::pair<boost::filesystem::path, std::string> >::iterator it = directories.begin(); |
|---|
| 615 | it != directories.end(); ++it) |
|---|
| 616 | { |
|---|
| 617 | if (boost::filesystem::exists(it->first) && !boost::filesystem::is_directory(it->first)) |
|---|
| 618 | { |
|---|
| 619 | ThrowException(General, std::string("The ") + it->second + " directory has been preoccupied by a file! \ |
|---|
| [2759] | 620 | Please remove " + it->first.string()); |
|---|
| [2710] | 621 | } |
|---|
| 622 | if (boost::filesystem::create_directories(it->first)) // function may not return true at all (bug?) |
|---|
| 623 | { |
|---|
| 624 | COUT(4) << "Created " << it->second << " directory" << std::endl; |
|---|
| 625 | } |
|---|
| 626 | } |
|---|
| 627 | } |
|---|
| [2896] | 628 | |
|---|
| [5695] | 629 | void Core::preUpdate(const Clock& time) |
|---|
| [2896] | 630 | { |
|---|
| 631 | } |
|---|
| [3370] | 632 | |
|---|
| [5695] | 633 | void Core::postUpdate(const Clock& time) |
|---|
| [3370] | 634 | { |
|---|
| 635 | } |
|---|
| [1505] | 636 | } |
|---|