Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Exported orxout() and the output levels to lua, replaces logMessage() and cout().
Note that OutputDefinitions.h is now included in the tolua section of core, even though the file is in util. But I guess that's ok.

  • Property svn:eol-style set to native
File size: 12.1 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/Output.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            orxout(internal_warning, context::lua) << "LuaState: Cannot include file '" << filename << "' (not found)." << 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            orxout(internal_warning, context::lua) << "LuaState: Cannot do file '" << filename << "' (not found)." << 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            orxout(internal_error, context::lua) << "Lua syntax error: " << lua_tostring(luaState_, -1) << endl;
179            break;
180        case LUA_ERRMEM:    // Memory allocation error
181            orxout(internal_error, context::lua) << "Lua memory allocation error: Consult your dentist immediately!" << 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                        orxout(internal_error, context::lua) << "Lua runtime error: " << errorString << endl;
203                }
204                break;
205            case LUA_ERRERR: // Error in the error handler
206                orxout(internal_error, context::lua) << "Lua error in error handler. No message available." << endl;
207                break;
208            case LUA_ERRMEM: // Memory allocation error
209                orxout(internal_error, context::lua) << "Lua memory allocation error: Consult your dentist immediately!" << 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::luaOutput(OutputLevel level, const std::string& context, const std::string& message)
239    {
240        orxout(level, registerContext(context)) << message << endl;
241    }
242
243    void LuaState::luaOutput(OutputLevel level, const std::string& message)
244    {
245        orxout(level, context::lua) << message << endl;
246    }
247
248    void LuaState::luaOutput(const std::string& message)
249    {
250        orxout(debug_output, context::lua) << message << endl;
251    }
252
253    bool LuaState::fileExists(const std::string& filename)
254    {
255        shared_ptr<ResourceInfo> info = this->getFileInfo(filename);
256        if (info == NULL)
257            return false;
258        else
259            return true;
260    }
261
262    //! Returns the content of a file
263    std::string LuaState::getSourceCode(const std::string& filename)
264    {
265        // Try the internal map first to get the actual Lua code
266        // and not just some pseudo Lua-XML code when using include* commands
267        std::map<std::string, std::string>::const_iterator it = this->sourceCodeMap_.find(filename);
268        if (it != this->sourceCodeMap_.end())
269            return it->second;
270        shared_ptr<ResourceInfo> info = Resource::getInfo(filename);
271        if (info == NULL)
272            return "";
273        else
274            return Resource::open(info)->getAsString();
275    }
276
277    bool LuaState::usingIOConsole() const
278    {
279        return IOConsole::exists();
280    }
281
282    /*static*/ LuaState::ToluaInterfaceMap& LuaState::getToluaInterfaces()
283    {
284        static ToluaInterfaceMap p;
285        return p;
286    }
287
288    /*static*/ std::vector<LuaState*>& LuaState::getInstances()
289    {
290        static std::vector<LuaState*> p;
291        return p;
292    }
293
294    /*static*/ bool LuaState::addToluaInterface(int (*function)(lua_State*), const std::string& name)
295    {
296        for (ToluaInterfaceMap::const_iterator it = getToluaInterfaces().begin(); it != getToluaInterfaces().end(); ++it)
297        {
298            if (it->first == name || it->second == function)
299            {
300                orxout(internal_warning, context::lua) << "Trying to add a Tolua interface with the same name or function." << endl;
301                return true;
302            }
303        }
304        getToluaInterfaces()[name] = function;
305
306        // Open interface in all LuaStates
307        for (std::vector<LuaState*>::const_iterator it = getInstances().begin(); it != getInstances().end(); ++it)
308            (*function)((*it)->luaState_);
309
310        // Return dummy bool
311        return true;
312    }
313
314    /*static*/ bool LuaState::removeToluaInterface(const std::string& name)
315    {
316        ToluaInterfaceMap::iterator it = getToluaInterfaces().find(name);
317        if (it == getToluaInterfaces().end())
318        {
319            orxout(internal_warning, context::lua) << "Cannot remove Tolua interface '" << name << "': Not found" << endl;
320            return true;
321        }
322
323        // Close interface in all LuaStates
324        for (std::vector<LuaState*>::const_iterator itState = getInstances().begin(); itState != getInstances().end(); ++itState)
325        {
326            lua_pushnil((*itState)->luaState_);
327            lua_setglobal((*itState)->luaState_, it->first.c_str());
328        }
329
330        // Remove entry
331        getToluaInterfaces().erase(it);
332
333        // Return dummy bool
334        return true;
335    }
336
337    /*static*/ void LuaState::openToluaInterfaces(lua_State* state)
338    {
339        for (ToluaInterfaceMap::const_iterator it = getToluaInterfaces().begin(); it != getToluaInterfaces().end(); ++it)
340            (*it->second)(state);
341    }
342
343    /*static*/ void LuaState::closeToluaInterfaces(lua_State* state)
344    {
345        for (ToluaInterfaceMap::const_iterator it = getToluaInterfaces().begin(); it != getToluaInterfaces().end(); ++it)
346        {
347            lua_pushnil(state);
348            lua_setglobal(state, it->first.c_str());
349        }
350    }
351
352
353    LuaFunctor::LuaFunctor(const std::string& code, LuaState* luaState)
354    {
355        this->code_ = code;
356        this->lua_ = luaState;
357    }
358
359    void LuaFunctor::operator()()
360    {
361        lua_->doString(this->code_);
362    }
363}
Note: See TracBrowser for help on using the repository browser.