Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/buildsystem3/src/core/Core.cc @ 2703

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

Fixed two typos

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