Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/libraries/core/LuaState.cc @ 6746

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

Merged gamestates2 branch back to trunk.
This brings in some heavy changes in the GUI framework.
It should also fix problems with triggered asserts in the InputManager.

Note: PickupInventory does not seem to work —> Segfault when showing because before, the owner in GUIOverlay::setGUIName is already NULL.
I haven't tested it before, so I can't tell whether it's my changes.

  • Property svn:eol-style set to native
File size: 12.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 *      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                if (errorHandler)
215                {
216                    // Do nothing (we already display the error in the
217                    // 'errorHandler' Lua function in LuaStateInit.lua)
218                }
219                else
220                {
221                    std::string errorString = lua_tostring(this->luaState_, -1);
222                    if (errorString.find("Error propagation") == std::string::npos)
223                        COUT(1) << "Lua runtime error: " << errorString << std::endl;
224                }
225                break;
226            case LUA_ERRERR: // Error in the error handler
227                COUT(1) << "Lua error in error handler. No message available." << std::endl;
228                break;
229            case LUA_ERRMEM: // Memory allocation error
230                COUT(1) << "Lua memory allocation error: Consult your dentist immediately!" << std::endl;
231                break;
232            }
233        }
234
235        if (error != 0)
236        {
237            lua_pop(luaState_, 1);  // Remove error message
238            lua_pushnil(luaState_); // Push a nil return value
239        }
240
241        if (errorHandler != 0)
242            lua_remove(luaState_, errorHandler); // Remove error handler from stack
243
244        // Set return value to a global variable because we cannot return a table in this function
245        // here. It would work for numbers, pointers and strings, but certainly not for Lua tables.
246        lua_setglobal(luaState_, "LuaStateReturnValue");
247
248        // Load the old info again
249        sourceFileInfo_ = oldSourceFileInfo;
250
251        return (error == 0);
252    }
253
254    void LuaState::luaPrint(const std::string& str)
255    {
256        output_ << str;
257    }
258
259    void LuaState::luaLog(unsigned int level, const std::string& message)
260    {
261        OutputHandler::getOutStream(level) << message << std::endl;
262    }
263
264    bool LuaState::fileExists(const std::string& filename)
265    {
266        shared_ptr<ResourceInfo> info = this->getFileInfo(filename);
267        if (info == NULL)
268            return false;
269        else
270            return true;
271    }
272
273    //! Returns the content of a file
274    std::string LuaState::getSourceCode(const std::string& filename)
275    {
276        // Try the internal map first to get the actual Lua code
277        // and not just some pseudo Lua-XML code when using include* commands
278        std::map<std::string, std::string>::const_iterator it = this->sourceCodeMap_.find(filename);
279        if (it != this->sourceCodeMap_.end())
280            return it->second;
281        shared_ptr<ResourceInfo> info = Resource::getInfo(filename);
282        if (info == NULL)
283            return "";
284        else
285            return Resource::open(info)->getAsString();
286    }
287
288    bool LuaState::usingIOConsole() const
289    {
290        return IOConsole::exists();
291    }
292
293#if LUA_VERSION_NUM != 501
294    const char * LuaState::lua_Chunkreader(lua_State *L, void *data, size_t *size)
295    {
296        LoadS* ls = static_cast<LoadS*>(data);
297        if (ls->size == 0)
298            return NULL;
299        *size = ls->size;
300        ls->size = 0;
301        return ls->s;
302    }
303#endif
304
305    /*static*/ bool LuaState::addToluaInterface(int (*function)(lua_State*), const std::string& name)
306    {
307        for (ToluaInterfaceMap::const_iterator it = toluaInterfaces_s.begin(); it != toluaInterfaces_s.end(); ++it)
308        {
309            if (it->first == name || it->second == function)
310            {
311                COUT(2) << "Warning: Trying to add a Tolua interface with the same name or function." << std::endl;
312                return true;
313            }
314        }
315        toluaInterfaces_s[name] = function;
316
317        // Open interface in all LuaStates
318        for (std::vector<LuaState*>::const_iterator it = instances_s.begin(); it != instances_s.end(); ++it)
319            (*function)((*it)->luaState_);
320
321        // Return dummy bool
322        return true;
323    }
324
325    /*static*/ bool LuaState::removeToluaInterface(const std::string& name)
326    {
327        ToluaInterfaceMap::iterator it = toluaInterfaces_s.find(name);
328        if (it == toluaInterfaces_s.end())
329        {
330            COUT(2) << "Warning: Cannot remove Tolua interface '" << name << "': Not found" << std::endl;
331            return true;
332        }
333
334        // Close interface in all LuaStates
335        for (std::vector<LuaState*>::const_iterator itState = instances_s.begin(); itState != instances_s.end(); ++itState)
336        {
337            lua_pushnil((*itState)->luaState_);
338            lua_setglobal((*itState)->luaState_, it->first.c_str());
339        }
340
341        // Remove entry
342        toluaInterfaces_s.erase(it);
343
344        // Return dummy bool
345        return true;
346    }
347
348    /*static*/ void LuaState::openToluaInterfaces(lua_State* state)
349    {
350        for (ToluaInterfaceMap::const_iterator it = toluaInterfaces_s.begin(); it != toluaInterfaces_s.end(); ++it)
351            (*it->second)(state);
352    }
353
354    /*static*/ void LuaState::closeToluaInterfaces(lua_State* state)
355    {
356        for (ToluaInterfaceMap::const_iterator it = toluaInterfaces_s.begin(); it != toluaInterfaces_s.end(); ++it)
357        {
358            lua_pushnil(state);
359            lua_setglobal(state, it->first.c_str());
360        }
361    }
362
363
364    LuaFunctor::LuaFunctor(const std::string& code, LuaState* luaState)
365    {
366        this->code_ = code;
367        this->lua_ = luaState;
368    }
369
370    void LuaFunctor::operator()(const MultiType& param1, const MultiType& param2, const MultiType& param3, const MultiType& param4, const MultiType& param5)
371    {
372        lua_->doString(this->code_);
373    }
374}
Note: See TracBrowser for help on using the repository browser.