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
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 *      Benjamin Knecht
24 *      Reto Grieder
25 *   Co-authors:
26 *      ...
27 *
28 */
29
30#include "LuaState.h"
31
32#include <tolua/tolua++.h>
33extern "C" {
34#include <lua.h>
35#include <lualib.h>
36}
37
38#include "util/Debug.h"
39#include "util/Exception.h"
40#include "util/ScopeGuard.h"
41#include "IOConsole.h"
42#include "Resource.h"
43#include "ToluaBindCore.h"
44
45namespace orxonox
46{
47    LuaState::ToluaInterfaceMap LuaState::toluaInterfaces_s;
48    std::vector<LuaState*> LuaState::instances_s;
49
50    // Do this after declaring toluaInterfaces_s and instances_s to avoid larger problems
51    DeclareToluaInterface(Core);
52
53    LuaState::LuaState()
54        : bIsRunning_(false)
55        , includeParseFunction_(NULL)
56    {
57        // Create new lua state and configure it
58        luaState_ = lua_open();
59        Loki::ScopeGuard luaStateGuard = Loki::MakeGuard(&lua_close, luaState_);
60#if LUA_VERSION_NUM == 501
61        luaL_openlibs(luaState_);
62#else
63        luaopen_base(luaState_);
64        luaopen_string(luaState_);
65        luaopen_table(luaState_);
66        luaopen_math(luaState_);
67        luaopen_io(luaState_);
68        luaopen_debug(luaState_);
69#endif
70
71        // Open all available tolua interfaces
72        this->openToluaInterfaces(luaState_);
73
74        // Create dummy file info
75        sourceFileInfo_.reset(new ResourceInfo());
76        sourceFileInfo_->group = "General";
77        sourceFileInfo_->size = 0;
78
79        // Push 'this' pointer
80        tolua_pushusertype(luaState_, static_cast<void*>(this), "orxonox::LuaState");
81        lua_setglobal(luaState_, "luaState");
82
83        // Parse init script
84        if (!this->doFile("LuaStateInit.lua"))
85            ThrowException(InitialisationFailed, "Running LuaStateInit.lua failed");
86
87        luaStateGuard.Dismiss();
88    }
89
90    LuaState::~LuaState()
91    {
92        lua_close(luaState_);
93    }
94
95    shared_ptr<ResourceInfo> LuaState::getFileInfo(const std::string& filename)
96    {
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);
102        return sourceInfo;
103    }
104
105    bool LuaState::includeFile(const std::string& filename)
106    {
107        shared_ptr<ResourceInfo> sourceInfo = this->getFileInfo(filename);
108        if (sourceInfo != NULL)
109            return this->includeString(Resource::open(sourceInfo)->getAsString(), sourceInfo);
110        else
111        {
112            COUT(2) << "LuaState: Cannot include file '" << filename << "' (not found)." << std::endl;
113            return false;
114        }
115    }
116
117    bool LuaState::includeString(const std::string& code, const shared_ptr<ResourceInfo>& sourceFileInfo)
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;
125
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;
143    }
144
145    bool LuaState::doFile(const std::string& filename)
146    {
147        shared_ptr<ResourceInfo> sourceInfo = this->getFileInfo(filename);
148        if (sourceInfo != NULL)
149            return this->doString(Resource::open(sourceInfo)->getAsString(), sourceInfo);
150        else
151        {
152            COUT(2) << "LuaState: Cannot do file '" << filename << "' (not found)." << std::endl;
153            return false;
154        }
155    }
156
157    bool LuaState::doString(const std::string& code, const shared_ptr<ResourceInfo>& sourceFileInfo)
158    {
159        // Save the old source file info
160        shared_ptr<ResourceInfo> oldSourceFileInfo = sourceFileInfo_;
161        // Only override if sourceFileInfo provides useful information
162        if (sourceFileInfo != NULL)
163            sourceFileInfo_ = sourceFileInfo;
164
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");
180        int errorHandler = lua_gettop(luaState_);
181        if (lua_isnil(this->luaState_, -1))
182        {
183            lua_pop(this->luaState_, 1);
184            errorHandler = 0;
185        }
186
187#if LUA_VERSION_NUM != 501
188        LoadS ls;
189        ls.s = code.c_str();
190        ls.size = code.size();
191        int error = lua_load(luaState_, &orxonox::LuaState::lua_Chunkreader, &ls, chunkname.c_str());
192#else
193        int error = luaL_loadbuffer(luaState_, code.c_str(), code.size(), chunkname.c_str());
194#endif
195
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
206        if (error == 0)
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
214                // Do nothing (we already display the error in the
215                // 'errorHandler' Lua function in LuaStateInit.lua)
216                break;
217            case LUA_ERRERR: // Error in the error handler
218                COUT(1) << "Lua error in error handler. No message available." << std::endl;
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
226        if (error != 0)
227        {
228            lua_pop(luaState_, 1);  // Remove error message
229            lua_pushnil(luaState_); // Push a nil return value
230        }
231
232        if (errorHandler != 0)
233            lua_remove(luaState_, errorHandler); // Remove error handler from stack
234
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.
237        lua_setglobal(luaState_, "LuaStateReturnValue");
238
239        // Load the old info again
240        sourceFileInfo_ = oldSourceFileInfo;
241
242        return (error == 0);
243    }
244
245    void LuaState::luaPrint(const std::string& str)
246    {
247        output_ << str;
248    }
249
250    void LuaState::luaLog(unsigned int level, const std::string& message)
251    {
252        OutputHandler::getOutStream(level) << message << std::endl;
253    }
254
255    bool LuaState::fileExists(const std::string& filename)
256    {
257        shared_ptr<ResourceInfo> info = this->getFileInfo(filename);
258        if (info == NULL)
259            return false;
260        else
261            return true;
262    }
263
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
284#if LUA_VERSION_NUM != 501
285    const char * LuaState::lua_Chunkreader(lua_State *L, void *data, size_t *size)
286    {
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;
293    }
294#endif
295
296    /*static*/ bool LuaState::addToluaInterface(int (*function)(lua_State*), const std::string& name)
297    {
298        for (ToluaInterfaceMap::const_iterator it = toluaInterfaces_s.begin(); it != toluaInterfaces_s.end(); ++it)
299        {
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            }
305        }
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;
314    }
315
316    /*static*/ bool LuaState::removeToluaInterface(const std::string& name)
317    {
318        ToluaInterfaceMap::iterator it = toluaInterfaces_s.find(name);
319        if (it == toluaInterfaces_s.end())
320        {
321            COUT(2) << "Warning: Cannot remove Tolua interface '" << name << "': Not found" << std::endl;
322            return true;
323        }
324
325        // Close interface in all LuaStates
326        for (std::vector<LuaState*>::const_iterator itState = instances_s.begin(); itState != instances_s.end(); ++itState)
327        {
328            lua_pushnil((*itState)->luaState_);
329            lua_setglobal((*itState)->luaState_, it->first.c_str());
330        }
331
332        // Remove entry
333        toluaInterfaces_s.erase(it);
334
335        // Return dummy bool
336        return true;
337    }
338
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    }
344
345    /*static*/ void LuaState::closeToluaInterfaces(lua_State* state)
346    {
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        }
352    }
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    }
365}
Note: See TracBrowser for help on using the repository browser.