Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/questsystem5/src/core/Core.cc @ 2930

Last change on this file since 2930 was 2908, checked in by dafrick, 16 years ago

Reverted to revision 2906 (because I'm too stupid to merge correctly, 2nd try will follow shortly. ;))

  • 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    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_, mediaPath_g.string())
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.string() + '/';
263    }
264
265    /*static*/ const boost::filesystem::path& Core::getConfigPath()
266    {
267        return configPath_g;
268    }
269    /*static*/ std::string Core::getConfigPathString()
270    {
271        return configPath_g.string() + '/';
272    }
273
274    /*static*/ const boost::filesystem::path& Core::getLogPath()
275    {
276        return logPath_g;
277    }
278    /*static*/ std::string Core::getLogPathString()
279    {
280        return logPath_g.string() + '/';
281    }
282
283    void Core::initializeRandomNumberGenerator()
284    {
285        static bool bInitialized = false;
286        if (!bInitialized && this->bInitializeRandomNumberGenerator_)
287        {
288            srand(time(0));
289            rand();
290            bInitialized = true;
291        }
292    }
293
294    /**
295    @brief
296        Performs the rather lower level operations just after
297        int main() has been called.
298    @remarks
299        This gets called AFTER pre-main stuff like AddFactory,
300        SetConsoleCommand, etc.
301    */
302    /*static*/ void Core::postMainInitialisation()
303    {
304        // set location of the executable
305        Core::setExecutablePath();
306
307        // Determine whether we have an installed or a binary dir run
308        // The latter occurs when simply running from the build directory
309        Core::checkDevBuild();
310
311        // Make sure the directories we write in exist or else make them
312        Core::createDirectories();
313    }
314
315    /**
316    @brief
317        Compares the executable path with the working directory
318    */
319    /*static*/ void Core::setExecutablePath()
320    {
321#ifdef ORXONOX_PLATFORM_WINDOWS
322        // get executable module
323        TCHAR buffer[1024];
324        if (GetModuleFileName(NULL, buffer, 1024) == 0)
325            ThrowException(General, "Could not retrieve executable path.");
326
327#elif defined(ORXONOX_PLATFORM_APPLE)
328        char buffer[1024];
329        unsigned long path_len = 1023;
330        if (_NSGetExecutablePath(buffer, &path_len))
331            ThrowException(General, "Could not retrieve executable path.");
332
333#else /* Linux */
334        /* written by Nicolai Haehnle <prefect_@gmx.net> */
335
336        /* Get our PID and build the name of the link in /proc */
337        char linkname[64]; /* /proc/<pid>/exe */
338        if (snprintf(linkname, sizeof(linkname), "/proc/%i/exe", getpid()) < 0)
339        {
340            /* This should only happen on large word systems. I'm not sure
341               what the proper response is here.
342               Since it really is an assert-like condition, aborting the
343               program seems to be in order. */
344            assert(false);
345        }
346
347        /* Now read the symbolic link */
348        char buffer[1024];
349        int ret;
350        ret = readlink(linkname, buffer, 1024);
351        /* In case of an error, leave the handling up to the caller */
352        if (ret == -1)
353            ThrowException(General, "Could not retrieve executable path.");
354
355        /* Ensure proper NUL termination */
356        buffer[ret] = 0;
357#endif
358
359        executablePath_g = boost::filesystem::path(buffer);
360#ifndef ORXONOX_PLATFORM_APPLE
361        executablePath_g = executablePath_g.branch_path(); // remove executable name
362#endif
363    }
364
365    /**
366    @brief
367        Checks for "orxonox_dev_build.keep_me" in the executable diretory.
368        If found it means that this is not an installed run, hence we
369        don't write the logs and config files to ~/.orxonox
370    */
371    /*static*/ void Core::checkDevBuild()
372    {
373        if (boost::filesystem::exists(executablePath_g / "orxonox_dev_build.keep_me"))
374        {
375            COUT(1) << "Running from the build tree." << std::endl;
376            Core::isDevBuild_s = true;
377            mediaPath_g  = ORXONOX_MEDIA_DEV_PATH;
378            configPath_g = ORXONOX_CONFIG_DEV_PATH;
379            logPath_g    = ORXONOX_LOG_DEV_PATH;
380        }
381        else
382        {
383#ifdef INSTALL_COPYABLE // --> relative paths
384            // Also set the root path
385            boost::filesystem::path relativeExecutablePath(ORXONOX_RUNTIME_INSTALL_PATH);
386            rootPath_g = executablePath_g;
387            while (!boost::filesystem::equivalent(rootPath_g / relativeExecutablePath, executablePath_g) || rootPath_g.empty())
388                rootPath_g = rootPath_g.branch_path();
389            if (rootPath_g.empty())
390                ThrowException(General, "Could not derive a root directory. Might the binary installation directory contain '..' when taken relative to the installation prefix path?");
391
392            // Using paths relative to the install prefix, complete them
393            mediaPath_g  = rootPath_g / ORXONOX_MEDIA_INSTALL_PATH;
394            configPath_g = rootPath_g / ORXONOX_CONFIG_INSTALL_PATH;
395            logPath_g    = rootPath_g / ORXONOX_LOG_INSTALL_PATH;
396#else
397            // There is no root path, so don't set it at all
398
399            mediaPath_g  = ORXONOX_MEDIA_INSTALL_PATH;
400
401            // Get user directory
402#  ifdef ORXONOX_PLATFORM_UNIX /* Apple? */
403            char* userDataPathPtr(getenv("HOME"));
404#  else
405            char* userDataPathPtr(getenv("APPDATA"));
406#  endif
407            if (userDataPathPtr == NULL)
408                ThrowException(General, "Could not retrieve user data path.");
409            boost::filesystem::path userDataPath(userDataPathPtr);
410            userDataPath /= ".orxonox";
411
412            configPath_g = userDataPath / ORXONOX_CONFIG_INSTALL_PATH;
413            logPath_g    = userDataPath / ORXONOX_LOG_INSTALL_PATH;
414#endif
415        }
416
417        // Option to put all the config and log files in a separate folder
418        if (!CommandLine::getArgument("directory")->hasDefaultValue())
419        {
420            std::string directory(CommandLine::getValue("directory").getString());
421            configPath_g = configPath_g / directory;
422            logPath_g    = logPath_g    / directory;
423        }
424    }
425
426    /*
427    @brief
428        Checks for the log and the config directory and creates them
429        if necessary. Otherwise me might have problems opening those files.
430    */
431    /*static*/ void Core::createDirectories()
432    {
433        std::vector<std::pair<boost::filesystem::path, std::string> > directories;
434        directories.push_back(std::pair<boost::filesystem::path, std::string>
435            (boost::filesystem::path(configPath_g), "config"));
436        directories.push_back(std::pair<boost::filesystem::path, std::string>
437            (boost::filesystem::path(logPath_g),    "log"));
438
439        for (std::vector<std::pair<boost::filesystem::path, std::string> >::iterator it = directories.begin();
440            it != directories.end(); ++it)
441        {
442            if (boost::filesystem::exists(it->first) && !boost::filesystem::is_directory(it->first))
443            {
444                ThrowException(General, std::string("The ") + it->second + " directory has been preoccupied by a file! \
445                                         Please remove " + it->first.string());
446            }
447            if (boost::filesystem::create_directories(it->first)) // function may not return true at all (bug?)
448            {
449                COUT(4) << "Created " << it->second << " directory" << std::endl;
450            }
451        }
452    }
453}
Note: See TracBrowser for help on using the repository browser.