Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/output/src/libraries/core/LuaState.cc @ 8796

Last change on this file since 8796 was 8796, checked in by landauf, 13 years ago

adjusted the rest of the code to the new output system, but some changes have to be reviewed.
all output is currently printed with debug level.
compiles again (posix console untested)

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