Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/gamestates2/src/libraries/core/LuaState.cc @ 6670

Last change on this file since 6670 was 6670, checked in by rgrieder, 14 years ago

Lua errors in 'doFile', 'includeFile' or 'require' should not be caught in theses functions but rather at the beginning of the Lua call.

  • Property svn:eol-style set to native
File size: 11.9 KB
RevLine 
[1959]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 *      Benjamin Knecht
[5654]24 *      Reto Grieder
[1959]25 *   Co-authors:
26 *      ...
27 *
28 */
29
[5654]30#include "LuaState.h"
[1959]31
[5654]32#include <tolua/tolua++.h>
[2710]33extern "C" {
[5654]34#include <lua.h>
[2710]35#include <lualib.h>
36}
37
[3196]38#include "util/Debug.h"
[6662]39#include "util/Exception.h"
40#include "util/ScopeGuard.h"
41#include "IOConsole.h"
[5781]42#include "Resource.h"
[5655]43#include "ToluaBindCore.h"
[1959]44
45namespace orxonox
46{
[5654]47    LuaState::ToluaInterfaceMap LuaState::toluaInterfaces_s;
48    std::vector<LuaState*> LuaState::instances_s;
[1959]49
[5655]50    // Do this after declaring toluaInterfaces_s and instances_s to avoid larger problems
51    DeclareToluaInterface(Core);
52
[5654]53    LuaState::LuaState()
54        : bIsRunning_(false)
55        , includeParseFunction_(NULL)
56    {
57        // Create new lua state and configure it
58        luaState_ = lua_open();
[6662]59        Loki::ScopeGuard luaStateGuard = Loki::MakeGuard(&lua_close, luaState_);
[1959]60#if LUA_VERSION_NUM == 501
[5654]61        luaL_openlibs(luaState_);
[1959]62#else
[5654]63        luaopen_base(luaState_);
64        luaopen_string(luaState_);
65        luaopen_table(luaState_);
66        luaopen_math(luaState_);
67        luaopen_io(luaState_);
68        luaopen_debug(luaState_);
[1959]69#endif
[3370]70
[5654]71        // Open all available tolua interfaces
72        this->openToluaInterfaces(luaState_);
[3370]73
[5654]74        // Create dummy file info
75        sourceFileInfo_.reset(new ResourceInfo());
[5781]76        sourceFileInfo_->group = "General";
77        sourceFileInfo_->size = 0;
[1959]78
[5759]79        // Push 'this' pointer
[5654]80        tolua_pushusertype(luaState_, static_cast<void*>(this), "orxonox::LuaState");
81        lua_setglobal(luaState_, "luaState");
[3370]82
[5654]83        // Parse init script
[6662]84        if (!this->doFile("LuaStateInit.lua"))
85            ThrowException(InitialisationFailed, "Running LuaStateInit.lua failed");
86
87        luaStateGuard.Dismiss();
[5654]88    }
[1959]89
[5654]90    LuaState::~LuaState()
91    {
92        lua_close(luaState_);
93    }
[2710]94
[6417]95    shared_ptr<ResourceInfo> LuaState::getFileInfo(const std::string& filename)
[5654]96    {
[6417]97        // Look in the current directory first
98        shared_ptr<ResourceInfo> sourceInfo = Resource::getInfo(sourceFileInfo_->path + filename);
99        // Continue search in root directories
100        if (sourceInfo == NULL && !sourceFileInfo_->path.empty())
101            sourceInfo = Resource::getInfo(filename);
[5654]102        return sourceInfo;
103    }
104
[6662]105    bool LuaState::includeFile(const std::string& filename)
[1959]106    {
[6417]107        shared_ptr<ResourceInfo> sourceInfo = this->getFileInfo(filename);
[5654]108        if (sourceInfo != NULL)
[6662]109            return this->includeString(Resource::open(sourceInfo)->getAsString(), sourceInfo);
[5654]110        else
[6662]111        {
112            COUT(2) << "LuaState: Cannot include file '" << filename << "' (not found)." << std::endl;
113            return false;
114        }
[1959]115    }
116
[6662]117    bool LuaState::includeString(const std::string& code, const shared_ptr<ResourceInfo>& sourceFileInfo)
[5654]118    {
119        // Parse string with provided include parser (otherwise don't preparse at all)
120        std::string luaInput;
121        if (includeParseFunction_ != NULL)
122            luaInput = (*includeParseFunction_)(code);
123        else
124            luaInput = code;
[1959]125
[6662]126        if (sourceFileInfo != NULL)
127        {
128            // Also fill a map with the actual source code. This is just for the include* commands
129            // where the content of sourceFileInfo->filename doesn't match 'code'
130            this->sourceCodeMap_[sourceFileInfo->filename] = code;
131        }
132
133        bool returnValue = this->doString(luaInput, sourceFileInfo);
134
135        if (sourceFileInfo != NULL)
136        {
137            // Delete source code entry
138            if (sourceFileInfo != NULL)
139                this->sourceCodeMap_.erase(sourceFileInfo->filename);
140        }
141
142        return returnValue;
[5654]143    }
144
[6662]145    bool LuaState::doFile(const std::string& filename)
[1959]146    {
[6417]147        shared_ptr<ResourceInfo> sourceInfo = this->getFileInfo(filename);
[5654]148        if (sourceInfo != NULL)
[6662]149            return this->doString(Resource::open(sourceInfo)->getAsString(), sourceInfo);
[5654]150        else
[6662]151        {
152            COUT(2) << "LuaState: Cannot do file '" << filename << "' (not found)." << std::endl;
153            return false;
154        }
[1959]155    }
156
[6662]157    bool LuaState::doString(const std::string& code, const shared_ptr<ResourceInfo>& sourceFileInfo)
[5654]158    {
[6417]159        // Save the old source file info
[5654]160        shared_ptr<ResourceInfo> oldSourceFileInfo = sourceFileInfo_;
161        // Only override if sourceFileInfo provides useful information
162        if (sourceFileInfo != NULL)
163            sourceFileInfo_ = sourceFileInfo;
[1959]164
[6662]165        std::string chunkname;
166        if (sourceFileInfo != NULL)
167        {
168            // Provide lua_load with the filename for debug purposes
169            // The '@' is a Lua convention to identify the chunk name as filename
170            chunkname = '@' + sourceFileInfo->filename;
171        }
172        else
173        {
174            // Use the code string to identify the chunk
175            chunkname = code;
176        }
177
178        // Push custom error handler that uses the debugger
179        lua_getglobal(this->luaState_, "errorHandler");
[6667]180        int errorHandler = lua_gettop(luaState_);
[6662]181        if (lua_isnil(this->luaState_, -1))
182        {
183            lua_pop(this->luaState_, 1);
184            errorHandler = 0;
185        }
186
[1959]187#if LUA_VERSION_NUM != 501
[5654]188        LoadS ls;
189        ls.s = code.c_str();
190        ls.size = code.size();
[6662]191        int error = lua_load(luaState_, &orxonox::LuaState::lua_Chunkreader, &ls, chunkname.c_str());
[5654]192#else
[6662]193        int error = luaL_loadbuffer(luaState_, code.c_str(), code.size(), chunkname.c_str());
[1959]194#endif
[5654]195
[6662]196        switch (error)
197        {
198        case LUA_ERRSYNTAX: // Syntax error
199            COUT(1) << "Lua syntax error: " << lua_tostring(luaState_, -1) << std::endl;
200            break;
201        case LUA_ERRMEM:    // Memory allocation error
202            COUT(1) << "Lua memory allocation error: Consult your dentist immediately!" << std::endl;
203            break;
204        }
205
[5654]206        if (error == 0)
[6662]207        {
208            // Execute the chunk in protected mode with an error handler function (stack index)
209            error = lua_pcall(luaState_, 0, 1, errorHandler);
210
211            switch (error)
212            {
213            case LUA_ERRRUN: // Runtime error
[6670]214                // Do nothing (we already display the error in the
[6662]215                // 'errorHandler' Lua function in LuaStateInit.lua)
216                break;
217            case LUA_ERRERR: // Error in the error handler
[6667]218                COUT(1) << "Lua error in error handler. No message available." << std::endl;
[6662]219                break;
220            case LUA_ERRMEM: // Memory allocation error
221                COUT(1) << "Lua memory allocation error: Consult your dentist immediately!" << std::endl;
222                break;
223            }
224        }
225
[5654]226        if (error != 0)
[6670]227        {
228            lua_pop(luaState_, 1);  // Remove error message
[6667]229            lua_pushnil(luaState_); // Push a nil return value
[6670]230        }
[6662]231
[6667]232        if (errorHandler != 0)
233            lua_remove(luaState_, errorHandler); // Remove error handler from stack
234
[6662]235        // Set return value to a global variable because we cannot return a table in this function
236        // here. It would work for numbers, pointers and strings, but certainly not for Lua tables.
[5661]237        lua_setglobal(luaState_, "LuaStateReturnValue");
[5654]238
239        // Load the old info again
240        sourceFileInfo_ = oldSourceFileInfo;
[6662]241
242        return (error == 0);
[1959]243    }
[5654]244
245    void LuaState::luaPrint(const std::string& str)
[1959]246    {
[5654]247        output_ << str;
[1959]248    }
249
[5654]250    void LuaState::luaLog(unsigned int level, const std::string& message)
[1959]251    {
[6105]252        OutputHandler::getOutStream(level) << message << std::endl;
[1959]253    }
[5654]254
[6417]255    bool LuaState::fileExists(const std::string& filename)
[5661]256    {
[6417]257        shared_ptr<ResourceInfo> info = this->getFileInfo(filename);
[5661]258        if (info == NULL)
259            return false;
260        else
261            return true;
262    }
263
[6662]264    //! Returns the content of a file
265    std::string LuaState::getSourceCode(const std::string& filename)
266    {
267        // Try the internal map first to get the actual Lua code
268        // and not just some pseudo Lua-XML code when using include* commands
269        std::map<std::string, std::string>::const_iterator it = this->sourceCodeMap_.find(filename);
270        if (it != this->sourceCodeMap_.end())
271            return it->second;
272        shared_ptr<ResourceInfo> info = Resource::getInfo(filename);
273        if (info == NULL)
274            return "";
275        else
276            return Resource::open(info)->getAsString();
277    }
278
279    bool LuaState::usingIOConsole() const
280    {
281        return IOConsole::exists();
282    }
283
[5654]284#if LUA_VERSION_NUM != 501
285    const char * LuaState::lua_Chunkreader(lua_State *L, void *data, size_t *size)
[1959]286    {
[5654]287        LoadS* ls = static_cast<LoadS*>(data);
288        if (ls->size == 0)
289            return NULL;
290        *size = ls->size;
291        ls->size = 0;
292        return ls->s;
[1959]293    }
[5654]294#endif
[1959]295
[5654]296    /*static*/ bool LuaState::addToluaInterface(int (*function)(lua_State*), const std::string& name)
[1959]297    {
[5654]298        for (ToluaInterfaceMap::const_iterator it = toluaInterfaces_s.begin(); it != toluaInterfaces_s.end(); ++it)
[1959]299        {
[5654]300            if (it->first == name || it->second == function)
301            {
302                COUT(2) << "Warning: Trying to add a Tolua interface with the same name or function." << std::endl;
303                return true;
304            }
[1959]305        }
[5654]306        toluaInterfaces_s[name] = function;
307
308        // Open interface in all LuaStates
309        for (std::vector<LuaState*>::const_iterator it = instances_s.begin(); it != instances_s.end(); ++it)
310            (*function)((*it)->luaState_);
311
312        // Return dummy bool
313        return true;
[1959]314    }
315
[5654]316    /*static*/ bool LuaState::removeToluaInterface(const std::string& name)
[1959]317    {
[5654]318        ToluaInterfaceMap::iterator it = toluaInterfaces_s.find(name);
319        if (it == toluaInterfaces_s.end())
[1959]320        {
[5654]321            COUT(2) << "Warning: Cannot remove Tolua interface '" << name << "': Not found" << std::endl;
322            return true;
[1959]323        }
324
[5654]325        // Close interface in all LuaStates
326        for (std::vector<LuaState*>::const_iterator itState = instances_s.begin(); itState != instances_s.end(); ++itState)
[1959]327        {
[5654]328            lua_pushnil((*itState)->luaState_);
329            lua_setglobal((*itState)->luaState_, it->first.c_str());
[1959]330        }
331
[5654]332        // Remove entry
333        toluaInterfaces_s.erase(it);
334
335        // Return dummy bool
336        return true;
[1959]337    }
338
[5654]339    /*static*/ void LuaState::openToluaInterfaces(lua_State* state)
340    {
341        for (ToluaInterfaceMap::const_iterator it = toluaInterfaces_s.begin(); it != toluaInterfaces_s.end(); ++it)
342            (*it->second)(state);
343    }
[1959]344
[5654]345    /*static*/ void LuaState::closeToluaInterfaces(lua_State* state)
[3370]346    {
[5654]347        for (ToluaInterfaceMap::const_iterator it = toluaInterfaces_s.begin(); it != toluaInterfaces_s.end(); ++it)
348        {
349            lua_pushnil(state);
350            lua_setglobal(state, it->first.c_str());
351        }
[3370]352    }
[6417]353
354
355    LuaFunctor::LuaFunctor(const std::string& code, LuaState* luaState)
356    {
357        this->code_ = code;
358        this->lua_ = luaState;
359    }
360
361    void LuaFunctor::operator()(const MultiType& param1, const MultiType& param2, const MultiType& param3, const MultiType& param4, const MultiType& param5)
362    {
363        lua_->doString(this->code_);
364    }
[1959]365}
Note: See TracBrowser for help on using the repository browser.