Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/unity_build/src/libraries/core/LuaState.cc @ 8740

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

Removed the need to declare the tolua interface explicitly (DeclareToluaInterface).
This is now automatically done in the ToluaBindLibrary.cc files.
That also removes the need for tolua bind header files.

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