Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/core/Core.cc @ 2710

Last change on this file since 2710 was 2710, checked in by rgrieder, 15 years ago

Merged buildsystem3 containing buildsystem2 containing Adi's buildsystem branch back to the trunk.
Please update the media directory if you were not using buildsystem3 before.

  • Property svn:eol-style set to native
File size: 16.3 KB
Line 
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
24 *   Co-authors:
25 *      Reto Grieder
26 *
27 */
28
29/**
30    @file
31    @brief Implementation of the Core class.
32*/
33
34#include "Core.h"
35
36#include <cassert>
37#include <fstream>
38#include <cstdlib>
39#include <cstdio>
40#include <boost/filesystem.hpp>
41
42#ifdef ORXONOX_PLATFORM_WINDOWS
43#  include <windows.h>
44#elif defined(ORXONOX_PLATFORM_APPLE)
45#  include <sys/param.h>
46#  include <mach-o/dyld.h>
47#else /* Linux */
48#  include <sys/types.h>
49#  include <unistd.h>
50#endif
51
52#include "SpecialConfig.h"
53#include "util/Exception.h"
54#include "Language.h"
55#include "CoreIncludes.h"
56#include "ConfigValueIncludes.h"
57#include "LuaBind.h"
58#include "CommandLine.h"
59
60namespace orxonox
61{
62    //! Path to the parent directory of the ones above if program was installed with relativ pahts
63    static boost::filesystem::path rootPath_g;
64    static boost::filesystem::path executablePath_g;            //!< Path to the executable
65    static boost::filesystem::path mediaPath_g;                 //!< Path to the media file folder
66    static boost::filesystem::path configPath_g;                //!< Path to the config file folder
67    static boost::filesystem::path logPath_g;                   //!< Path to the log file folder
68
69    bool Core::bShowsGraphics_s = false;
70    bool Core::bHasServer_s     = false;
71    bool Core::bIsClient_s      = false;
72    bool Core::bIsStandalone_s  = false;
73    bool Core::bIsMaster_s      = false;
74
75    bool Core::isDevBuild_s     = false;
76    Core* Core::singletonRef_s  = 0;
77
78    SetCommandLineArgument(mediaPath, "").information("PATH");
79    SetCommandLineArgument(directory, "").information("DIR");
80
81    /**
82        @brief Constructor: Registers the object and sets the config-values.
83        @param A reference to a global variable, used to avoid an infinite recursion in getSoftDebugLevel()
84    */
85    Core::Core()
86    {
87        RegisterRootObject(Core);
88
89        assert(Core::singletonRef_s == 0);
90        Core::singletonRef_s = this;
91
92        this->bInitializeRandomNumberGenerator_ = false;
93        this->setConfigValues();
94
95        // Set the correct log path. Before this call, /tmp (Unix) or %TEMP% was used
96        OutputHandler::getOutStream().setLogPath(Core::getLogPathString());
97
98        // Possible media path override by the command line
99        if (!CommandLine::getArgument("mediaPath")->hasDefaultValue())
100        {
101            //std::string mediaPath = CommandLine::getValue("mediaPath");
102            Core::tsetMediaPath(CommandLine::getValue("mediaPath"));
103        }
104    }
105
106    /**
107        @brief Sets the bool to true to avoid static functions accessing a deleted object.
108    */
109    Core::~Core()
110    {
111        assert(Core::singletonRef_s);
112        Core::singletonRef_s = 0;
113    }
114
115    /**
116        @brief Function to collect the SetConfigValue-macro calls.
117    */
118    void Core::setConfigValues()
119    {
120#ifdef NDEBUG
121        const unsigned int defaultLevelConsole = 1;
122        const unsigned int defaultLevelLogfile = 3;
123        const unsigned int defaultLevelShell   = 1;
124#else
125        const unsigned int defaultLevelConsole = 3;
126        const unsigned int defaultLevelLogfile = 4;
127        const unsigned int defaultLevelShell   = 3;
128#endif
129        SetConfigValue(softDebugLevelConsole_, defaultLevelConsole)
130            .description("The maximal level of debug output shown in the console").callback(this, &Core::debugLevelChanged);
131        SetConfigValue(softDebugLevelLogfile_, defaultLevelLogfile)
132            .description("The maximal level of debug output shown in the logfile").callback(this, &Core::debugLevelChanged);
133        SetConfigValue(softDebugLevelShell_, defaultLevelShell)
134            .description("The maximal level of debug output shown in the ingame shell").callback(this, &Core::debugLevelChanged);
135
136        SetConfigValue(language_, Language::getLanguage().defaultLanguage_).description("The language of the ingame text").callback(this, &Core::languageChanged);
137        SetConfigValue(bInitializeRandomNumberGenerator_, true).description("If true, all random actions are different each time you start the game").callback(this, &Core::initializeRandomNumberGenerator);
138
139        SetConfigValue(mediaPathString_, Core::getMediaPathPOSIXString())
140            .description("Relative path to the game data.").callback(this, &Core::mediaPathChanged);
141    }
142
143    /**
144        @brief Callback function if the debug level has changed.
145    */
146    void Core::debugLevelChanged()
147    {
148        // softDebugLevel_ is the maximum of the 3 variables
149        this->softDebugLevel_ = this->softDebugLevelConsole_;
150        if (this->softDebugLevelLogfile_ > this->softDebugLevel_)
151            this->softDebugLevel_ = this->softDebugLevelLogfile_;
152        if (this->softDebugLevelShell_ > this->softDebugLevel_)
153            this->softDebugLevel_ = this->softDebugLevelShell_;
154
155        OutputHandler::setSoftDebugLevel(OutputHandler::LD_All,     this->softDebugLevel_);
156        OutputHandler::setSoftDebugLevel(OutputHandler::LD_Console, this->softDebugLevelConsole_);
157        OutputHandler::setSoftDebugLevel(OutputHandler::LD_Logfile, this->softDebugLevelLogfile_);
158        OutputHandler::setSoftDebugLevel(OutputHandler::LD_Shell,   this->softDebugLevelShell_);
159    }
160
161    /**
162        @brief Callback function if the language has changed.
163    */
164    void Core::languageChanged()
165    {
166        // Read the translation file after the language was configured
167        Language::getLanguage().readTranslatedLanguageFile();
168    }
169
170    /**
171    @brief
172        Callback function if the media path has changed.
173    */
174    void Core::mediaPathChanged()
175    {
176        mediaPath_g = boost::filesystem::path(this->mediaPathString_);
177    }
178
179    /**
180        @brief Returns the softDebugLevel for the given device (returns a default-value if the class ist right about to be created).
181        @param device The device
182        @return The softDebugLevel
183    */
184    int Core::getSoftDebugLevel(OutputHandler::OutputDevice device)
185    {
186        switch (device)
187        {
188        case OutputHandler::LD_All:
189            return Core::getInstance().softDebugLevel_;
190        case OutputHandler::LD_Console:
191            return Core::getInstance().softDebugLevelConsole_;
192        case OutputHandler::LD_Logfile:
193            return Core::getInstance().softDebugLevelLogfile_;
194        case OutputHandler::LD_Shell:
195            return Core::getInstance().softDebugLevelShell_;
196        default:
197            assert(0);
198            return 2;
199        }
200    }
201
202     /**
203        @brief Sets the softDebugLevel for the given device. Please use this only temporary and restore the value afterwards, as it overrides the configured value.
204        @param device The device
205        @param level The level
206    */
207     void Core::setSoftDebugLevel(OutputHandler::OutputDevice device, int level)
208     {
209        if (device == OutputHandler::LD_All)
210            Core::getInstance().softDebugLevel_ = level;
211        else if (device == OutputHandler::LD_Console)
212            Core::getInstance().softDebugLevelConsole_ = level;
213        else if (device == OutputHandler::LD_Logfile)
214            Core::getInstance().softDebugLevelLogfile_ = level;
215        else if (device == OutputHandler::LD_Shell)
216            Core::getInstance().softDebugLevelShell_ = level;
217
218        OutputHandler::setSoftDebugLevel(device, level);
219     }
220
221    /**
222        @brief Returns the configured language.
223    */
224    const std::string& Core::getLanguage()
225    {
226        return Core::getInstance().language_;
227    }
228
229    /**
230        @brief Sets the language in the config-file back to the default.
231    */
232    void Core::resetLanguage()
233    {
234        Core::getInstance().resetLanguageIntern();
235    }
236
237    /**
238        @brief Sets the language in the config-file back to the default.
239    */
240    void Core::resetLanguageIntern()
241    {
242        ResetConfigValue(language_);
243    }
244
245    /**
246    @brief
247        Temporary sets the media path
248    @param path
249        The new media path
250    */
251    void Core::_tsetMediaPath(const std::string& path)
252    {
253        ModifyConfigValue(mediaPathString_, tset, path);
254    }
255
256    /*static*/ const boost::filesystem::path& Core::getMediaPath()
257    {
258        return mediaPath_g;
259    }
260    /*static*/ std::string Core::getMediaPathString()
261    {
262        return mediaPath_g.directory_string() + CP_SLASH;
263    }
264    /*static*/ std::string Core::getMediaPathPOSIXString()
265    {
266        return mediaPath_g.string() + '/';
267       
268    }
269
270    /*static*/ const boost::filesystem::path& Core::getConfigPath()
271    {
272        return configPath_g;
273    }
274    /*static*/ std::string Core::getConfigPathString()
275    {
276        return configPath_g.directory_string() + CP_SLASH;
277    }
278    /*static*/ std::string Core::getConfigPathPOSIXString()
279    {
280        return configPath_g.string() + '/';
281    }
282
283    /*static*/ const boost::filesystem::path& Core::getLogPath()
284    {
285        return logPath_g;
286    }
287    /*static*/ std::string Core::getLogPathString()
288    {
289        return logPath_g.directory_string() + CP_SLASH;
290    }
291    /*static*/ std::string Core::getLogPathPOSIXString()
292    {
293        return logPath_g.string() + '/';
294    }
295
296    void Core::initializeRandomNumberGenerator()
297    {
298        static bool bInitialized = false;
299        if (!bInitialized && this->bInitializeRandomNumberGenerator_)
300        {
301            srand(time(0));
302            rand();
303            bInitialized = true;
304        }
305    }
306
307    /**
308    @brief
309        Performs the rather lower level operations just after
310        int main() has been called.
311    @remarks
312        This gets called AFTER pre-main stuff like AddFactory,
313        SetConsoleCommand, etc.
314    */
315    /*static*/ void Core::postMainInitialisation()
316    {
317        // set location of the executable
318        Core::setExecutablePath();
319
320        // Determine whether we have an installed or a binary dir run
321        // The latter occurs when simply running from the build directory
322        Core::checkDevBuild();
323
324        // Make sure the directories we write in exist or else make them
325        Core::createDirectories();
326    }
327
328    /**
329    @brief
330        Compares the executable path with the working directory
331    */
332    /*static*/ void Core::setExecutablePath()
333    {
334#ifdef ORXONOX_PLATFORM_WINDOWS
335        // get executable module
336        TCHAR buffer[1024];
337        if (GetModuleFileName(NULL, buffer, 1024) == 0)
338            ThrowException(General, "Could not retrieve executable path.");
339
340#elif defined(ORXONOX_PLATFORM_APPLE)
341        char buffer[1024];
342        unsigned long path_len = 1023;
343        if (_NSGetExecutablePath(buffer, &path_len))
344            ThrowException(General, "Could not retrieve executable path.");
345
346#else /* Linux */
347        /* written by Nicolai Haehnle <prefect_@gmx.net> */
348
349        /* Get our PID and build the name of the link in /proc */
350        char linkname[64]; /* /proc/<pid>/exe */
351        if (snprintf(linkname, sizeof(linkname), "/proc/%i/exe", getpid()) < 0)
352        {
353            /* This should only happen on large word systems. I'm not sure
354               what the proper response is here.
355               Since it really is an assert-like condition, aborting the
356               program seems to be in order. */
357            assert(false);
358        }
359
360        /* Now read the symbolic link */
361        char buffer[1024];
362        int ret;
363        ret = readlink(linkname, buffer, 1024);
364        /* In case of an error, leave the handling up to the caller */
365        if (ret == -1)
366            ThrowException(General, "Could not retrieve executable path.");
367
368        /* Ensure proper NUL termination */
369        buffer[ret] = 0;
370#endif
371
372        executablePath_g = boost::filesystem::path(buffer);
373#ifndef ORXONOX_PLATFORM_APPLE
374        executablePath_g = executablePath_g.branch_path(); // remove executable name
375#endif
376    }
377
378    /**
379    @brief
380        Checks for "orxonox_dev_build.keep_me" in the executable diretory.
381        If found it means that this is not an installed run, hence we
382        don't write the logs and config files to ~/.orxonox
383    */
384    /*static*/ void Core::checkDevBuild()
385    {
386        if (boost::filesystem::exists(executablePath_g / "orxonox_dev_build.keep_me"))
387        {
388            COUT(1) << "Running from the build tree." << std::endl;
389            Core::isDevBuild_s = true;
390            mediaPath_g  = ORXONOX_MEDIA_DEV_PATH;
391            configPath_g = ORXONOX_CONFIG_DEV_PATH;
392            logPath_g    = ORXONOX_LOG_DEV_PATH;
393        }
394        else
395        {
396#ifdef INSTALL_COPYABLE // --> relative paths
397            // Also set the root path
398            boost::filesystem::path relativeExecutablePath(ORXONOX_RUNTIME_INSTALL_PATH);
399            rootPath_g = executablePath_g;
400            while (!boost::filesystem::equivalent(rootPath_g / relativeExecutablePath, executablePath_g) || rootPath_g.empty())
401                rootPath_g = rootPath_g.branch_path();
402            if (rootPath_g.empty())
403                ThrowException(General, "Could not derive a root directory. Might the binary installation directory contain '..' when taken relative to the installation prefix path?");
404
405            // Using paths relative to the install prefix, complete them
406            mediaPath_g  = rootPath_g / ORXONOX_MEDIA_INSTALL_PATH;
407            configPath_g = rootPath_g / ORXONOX_CONFIG_INSTALL_PATH;
408            logPath_g    = rootPath_g / ORXONOX_LOG_INSTALL_PATH;
409#else
410            // There is no root path, so don't set it at all
411
412            mediaPath_g  = ORXONOX_MEDIA_INSTALL_PATH;
413
414            // Get user directory
415#  ifdef ORXONOX_PLATFORM_UNIX /* Apple? */
416            char* userDataPathPtr(getenv("HOME"));
417#  else
418            char* userDataPathPtr(getenv("APPDATA"));
419#  endif
420            if (userDataPathPtr == NULL)
421                ThrowException(General, "Could not retrieve user data path.");
422            boost::filesystem::path userDataPath(userDataPathPtr);
423            userDataPath /= ".orxonox";
424
425            configPath_g = userDataPath / ORXONOX_CONFIG_INSTALL_PATH;
426            logPath_g    = userDataPath / ORXONOX_LOG_INSTALL_PATH;
427#endif
428        }
429
430        // Option to put all the config and log files in a separate folder
431        if (!CommandLine::getArgument("directory")->hasDefaultValue())
432        {
433            std::string directory(CommandLine::getValue("directory"));
434            configPath_g = configPath_g / directory;
435            logPath_g    = logPath_g    / directory;
436        }
437    }
438
439    /*
440    @brief
441        Checks for the log and the config directory and creates them
442        if necessary. Otherwise me might have problems opening those files.
443    */
444    /*static*/ void Core::createDirectories()
445    {
446        std::vector<std::pair<boost::filesystem::path, std::string> > directories;
447        directories.push_back(std::pair<boost::filesystem::path, std::string>
448            (boost::filesystem::path(configPath_g), "config"));
449        directories.push_back(std::pair<boost::filesystem::path, std::string>
450            (boost::filesystem::path(logPath_g),    "log"));
451
452        for (std::vector<std::pair<boost::filesystem::path, std::string> >::iterator it = directories.begin();
453            it != directories.end(); ++it)
454        {
455            if (boost::filesystem::exists(it->first) && !boost::filesystem::is_directory(it->first))
456            {
457                ThrowException(General, std::string("The ") + it->second + " directory has been preoccupied by a file! \
458                                         Please remove " + it->first.file_string());
459            }
460            if (boost::filesystem::create_directories(it->first)) // function may not return true at all (bug?)
461            {
462                COUT(4) << "Created " << it->second << " directory" << std::endl;
463            }
464        }
465    }
466}
Note: See TracBrowser for help on using the repository browser.