Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/gamestates3/src/libraries/core/LuaState.cc @ 10035

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

Activated Strict.lua
That means you can no longer assign to or access an undeclared global variable on function scope.
Declaring: either assign anything to the variable (including nil) on file scope
or use the function global("myVar", "myVar2", …, "myVarN") (mind the strings!)

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