622 lines
17 KiB
C++
622 lines
17 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
||
*
|
||
* ScummVM is the legal property of its developers, whose names
|
||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||
* file distributed with this source distribution.
|
||
*
|
||
* This program is free software; you can redistribute it and/or
|
||
* modify it under the terms of the GNU General Public License
|
||
* as published by the Free Software Foundation; either version 2
|
||
* of the License, or (at your option) any later version.
|
||
|
||
* This program is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
* GNU General Public License for more details.
|
||
|
||
* You should have received a copy of the GNU General Public License
|
||
* along with this program; if not, write to the Free Software
|
||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
*
|
||
* $URL$
|
||
* $Id$
|
||
*
|
||
*/
|
||
|
||
/*
|
||
* This code is based on Broken Sword 2.5 engine
|
||
*
|
||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||
*
|
||
* Licensed under GNU GPL v2
|
||
*
|
||
*/
|
||
|
||
#define BS_LOG_PREFIX "LUA"
|
||
|
||
// -----------------------------------------------------------------------------
|
||
// Includes
|
||
// -----------------------------------------------------------------------------
|
||
|
||
extern "C"
|
||
{
|
||
#include "sword25/util/lua/lua.h"
|
||
#include "sword25/util/lua/lualib.h"
|
||
#include "sword25/util/lua/lauxlib.h"
|
||
#include "sword25/util/pluto/pluto.h"
|
||
}
|
||
|
||
#include "sword25/package/packagemanager.h"
|
||
#include "sword25/script/luascript.h"
|
||
#include "sword25/script/luabindhelper.h"
|
||
|
||
#include "sword25/kernel/outputpersistenceblock.h"
|
||
#include "sword25/kernel/inputpersistenceblock.h"
|
||
|
||
#include "sword25/kernel/memlog_off.h"
|
||
#include <vector>
|
||
#include "sword25/kernel/memlog_on.h"
|
||
|
||
using namespace std;
|
||
|
||
// -----------------------------------------------------------------------------
|
||
// Konstruktion / Destruktion
|
||
// -----------------------------------------------------------------------------
|
||
|
||
BS_LuaScriptEngine::BS_LuaScriptEngine(BS_Kernel * KernelPtr) :
|
||
BS_ScriptEngine(KernelPtr),
|
||
m_State(0),
|
||
m_PcallErrorhandlerRegistryIndex(0)
|
||
{
|
||
}
|
||
|
||
// -----------------------------------------------------------------------------
|
||
|
||
BS_LuaScriptEngine::~BS_LuaScriptEngine()
|
||
{
|
||
// Lua deinitialisieren
|
||
if (m_State) lua_close(m_State);
|
||
}
|
||
|
||
// -----------------------------------------------------------------------------
|
||
|
||
BS_Service * BS_LuaScriptEngine_CreateObject(BS_Kernel * KernelPtr) { return new BS_LuaScriptEngine(KernelPtr); }
|
||
|
||
// -----------------------------------------------------------------------------
|
||
|
||
namespace
|
||
{
|
||
int PanicCB(lua_State * L)
|
||
{
|
||
BS_LOG_ERRORLN("Lua panic. Error message: %s", lua_isnil(L, -1) ? "" : lua_tostring(L, -1));
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
// -----------------------------------------------------------------------------
|
||
|
||
bool BS_LuaScriptEngine::Init()
|
||
{
|
||
// Lua-State intialisieren und Standardbibliotheken initialisieren
|
||
m_State = luaL_newstate();
|
||
if (!m_State || ! RegisterStandardLibs() || !RegisterStandardLibExtensions())
|
||
{
|
||
BS_LOG_ERRORLN("Lua could not be initialized.");
|
||
return false;
|
||
}
|
||
|
||
// Panic-Callbackfunktion registrieren.
|
||
lua_atpanic(m_State, PanicCB);
|
||
|
||
// Errorhandlerfunktion f<>r lua_pcall-Aufrufe.
|
||
// Der untenstehende Code enth<74>lt eine lokale ErrorHandler-Funktion und gibt diese zur<75>ck.
|
||
const char ErrorHandlerCode[] =
|
||
"local function ErrorHandler(message) "
|
||
" return message .. '\\n' .. debug.traceback('', 2) "
|
||
"end "
|
||
"return ErrorHandler";
|
||
|
||
// Den Code compilieren.
|
||
if (luaL_loadbuffer(m_State, ErrorHandlerCode, strlen(ErrorHandlerCode), "PCALL ERRORHANDLER") != 0)
|
||
{
|
||
// Fehlernachricht ausgeben und Methode beenden.
|
||
BS_LOG_ERRORLN("Couldn't compile luaL_pcall errorhandler:\n%s", lua_tostring(m_State, -1));
|
||
lua_pop(m_State, 1);
|
||
|
||
return false;
|
||
}
|
||
// Den Code ausf<73>hren, dies legt die Errorhandler-Funktion oben auf den Stack.
|
||
if (lua_pcall(m_State, 0, 1, 0) != 0)
|
||
{
|
||
// Fehlernachricht ausgeben und Methode beenden.
|
||
BS_LOG_ERRORLN("Couldn't prepare luaL_pcall errorhandler:\n%s", lua_tostring(m_State, -1));
|
||
lua_pop(m_State, 1);
|
||
|
||
return false;
|
||
}
|
||
|
||
// Die Errorhandler-Funktion in der Lua-Registry ablegen und den Index merken.
|
||
m_PcallErrorhandlerRegistryIndex = luaL_ref(m_State, LUA_REGISTRYINDEX);
|
||
|
||
// Die Pluto Persistenz-Bibliothek initialisieren.
|
||
luaopen_pluto(m_State);
|
||
lua_pop(m_State, 1);
|
||
|
||
BS_LOGLN("Lua initialized.");
|
||
|
||
return true;
|
||
}
|
||
|
||
// -----------------------------------------------------------------------------
|
||
|
||
bool BS_LuaScriptEngine::ExecuteFile(const std::string & FileName)
|
||
{
|
||
#ifdef DEBUG
|
||
int __startStackDepth = lua_gettop(m_State);
|
||
#endif
|
||
|
||
// Pointer auf den Packagemanager holen
|
||
BS_PackageManager * pPackage = static_cast<BS_PackageManager *>(BS_Kernel::GetInstance()->GetService("package"));
|
||
BS_ASSERT(pPackage);
|
||
|
||
// Datei einlesen
|
||
unsigned int FileSize;
|
||
char * FileData = static_cast<char *>(pPackage->GetFile(FileName, &FileSize));
|
||
if (!FileData)
|
||
{
|
||
BS_LOG_ERRORLN("Couldn't read \"%s\".", FileName.c_str());
|
||
#ifdef DEBUG
|
||
BS_ASSERT(__startStackDepth == lua_gettop(m_State));
|
||
#endif
|
||
return false;
|
||
}
|
||
|
||
// Dateiinhalt ausf<73>hren
|
||
if (!ExecuteBuffer(FileData, FileSize, "@" + pPackage->GetAbsolutePath(FileName)))
|
||
{
|
||
// Dateipuffer freigeben
|
||
delete[] FileData;
|
||
#ifdef DEBUG
|
||
BS_ASSERT(__startStackDepth == lua_gettop(m_State));
|
||
#endif
|
||
return false;
|
||
}
|
||
|
||
// Dateipuffer freigeben
|
||
delete[] FileData;
|
||
|
||
#ifdef DEBUG
|
||
BS_ASSERT(__startStackDepth == lua_gettop(m_State));
|
||
#endif
|
||
|
||
return true;
|
||
}
|
||
|
||
// -----------------------------------------------------------------------------
|
||
|
||
bool BS_LuaScriptEngine::ExecuteString(const std::string & Code)
|
||
{
|
||
return ExecuteBuffer(Code.c_str(), Code.length(), "???");
|
||
}
|
||
|
||
// -----------------------------------------------------------------------------
|
||
|
||
namespace
|
||
{
|
||
void RemoveForbiddenFunctions(lua_State * L)
|
||
{
|
||
static const char * FORBIDDEN_FUNCTIONS[] =
|
||
{
|
||
"dofile",
|
||
0
|
||
};
|
||
|
||
const char ** Iterator = FORBIDDEN_FUNCTIONS;
|
||
while (*Iterator)
|
||
{
|
||
lua_pushnil(L);
|
||
lua_setfield(L, LUA_GLOBALSINDEX, *Iterator);
|
||
++Iterator;
|
||
}
|
||
}
|
||
}
|
||
|
||
bool BS_LuaScriptEngine::RegisterStandardLibs()
|
||
{
|
||
luaL_openlibs(m_State);
|
||
RemoveForbiddenFunctions(m_State);
|
||
return true;
|
||
}
|
||
|
||
// -----------------------------------------------------------------------------
|
||
|
||
bool BS_LuaScriptEngine::ExecuteBuffer(const char * Data, unsigned int Size, const std::string & Name) const
|
||
{
|
||
// Puffer kompilieren
|
||
if (luaL_loadbuffer(m_State, Data, Size, Name.c_str()) != 0)
|
||
{
|
||
BS_LOG_ERRORLN("Couldn't compile \"%s\":\n%s", Name.c_str(), lua_tostring(m_State, -1));
|
||
lua_pop(m_State, 1);
|
||
|
||
return false;
|
||
}
|
||
|
||
// Error-Handler Funktion hinter der auszuf<75>hrenden Funktion auf den Stack legen.
|
||
lua_rawgeti(m_State, LUA_REGISTRYINDEX, m_PcallErrorhandlerRegistryIndex);
|
||
lua_insert(m_State, -2);
|
||
|
||
// Pufferinhalt ausf<73>hren
|
||
if (lua_pcall(m_State, 0, 0, -2) != 0)
|
||
{
|
||
BS_LOG_ERRORLN("An error occured while executing \"%s\":\n%s.",
|
||
Name.c_str(),
|
||
lua_tostring(m_State, -1));
|
||
lua_pop(m_State, 2);
|
||
|
||
return false;
|
||
}
|
||
|
||
// Error-Handler Funktion vom Stack nehmen.
|
||
lua_pop(m_State, 1);
|
||
|
||
return true;
|
||
}
|
||
|
||
// -----------------------------------------------------------------------------
|
||
|
||
void BS_LuaScriptEngine::SetCommandLine(const vector<string> & CommandLineParameters)
|
||
{
|
||
lua_newtable(m_State);
|
||
|
||
for (size_t i = 0; i < CommandLineParameters.size(); ++i)
|
||
{
|
||
lua_pushnumber(m_State, i + 1);
|
||
lua_pushstring(m_State, CommandLineParameters[i].c_str());
|
||
lua_settable(m_State, -3);
|
||
}
|
||
|
||
lua_setglobal(m_State, "CommandLine");
|
||
}
|
||
|
||
// -----------------------------------------------------------------------------
|
||
|
||
namespace
|
||
{
|
||
const char * PERMANENTS_TABLE_NAME = "Permanents";
|
||
|
||
// -------------------------------------------------------------------------
|
||
|
||
// Diese Array enth<74>lt die Namen der globalen Lua-Objekte, die nicht persistiert werden sollen.
|
||
const char * STANDARD_PERMANENTS[] =
|
||
{
|
||
"string",
|
||
"xpcall",
|
||
"package",
|
||
"tostring",
|
||
"print",
|
||
"os",
|
||
"unpack",
|
||
"require",
|
||
"getfenv",
|
||
"setmetatable",
|
||
"next",
|
||
"assert",
|
||
"tonumber",
|
||
"io",
|
||
"rawequal",
|
||
"collectgarbage",
|
||
"getmetatable",
|
||
"module",
|
||
"rawset",
|
||
"warning",
|
||
"math",
|
||
"debug",
|
||
"pcall",
|
||
"table",
|
||
"newproxy",
|
||
"type",
|
||
"coroutine",
|
||
"select",
|
||
"gcinfo",
|
||
"pairs",
|
||
"rawget",
|
||
"loadstring",
|
||
"ipairs",
|
||
"_VERSION",
|
||
"setfenv",
|
||
"load",
|
||
"error",
|
||
"loadfile",
|
||
|
||
"pairs_next",
|
||
"ipairs_next",
|
||
"pluto",
|
||
"Cfg",
|
||
"Translator",
|
||
"Persistence",
|
||
"CommandLine",
|
||
0
|
||
};
|
||
|
||
// -------------------------------------------------------------------------
|
||
|
||
enum PERMANENT_TABLE_TYPE
|
||
{
|
||
PTT_PERSIST,
|
||
PTT_UNPERSIST
|
||
};
|
||
|
||
// -------------------------------------------------------------------------
|
||
|
||
bool PushPermanentsTable(lua_State * L, PERMANENT_TABLE_TYPE TableType)
|
||
{
|
||
// Permanents-Tabelle erstellen.
|
||
lua_newtable(L);
|
||
|
||
// Alle Standard-Permanents in die Tabelle einf<6E>gen.
|
||
unsigned int Index = 0;
|
||
while (STANDARD_PERMANENTS[Index])
|
||
{
|
||
// Permanent auf den Stack legen, falls es nicht existiert, wird es einfach ignoriert.
|
||
lua_getglobal(L, STANDARD_PERMANENTS[Index]);
|
||
if (!lua_isnil(L, -1))
|
||
{
|
||
// Namen des Elementes als einzigartigen Wert auf den Stack legen.
|
||
lua_pushstring(L, STANDARD_PERMANENTS[Index]);
|
||
|
||
// Falls geladen wird, ist der Name der Index und das Objekt der Wert.
|
||
// In diesem Fall m<>ssen also die Position von Name und Objekt auf dem Stack vertauscht werden.
|
||
if (TableType == PTT_UNPERSIST) lua_insert(L, -2);
|
||
|
||
// Eintrag in der Tabelle vornehmen.
|
||
lua_settable(L, -3);
|
||
}
|
||
else
|
||
{
|
||
// nil von Stack poppen.
|
||
lua_pop(L, 1);
|
||
}
|
||
|
||
++Index;
|
||
}
|
||
|
||
// Alle Registrierten C-Funktionen in die Tabelle einf<6E>gen.
|
||
// BS_LuaBindhelper legt in der Registry eine Tabelle an, in der alle registrierten C-Funktionen gespeichert werden.
|
||
|
||
// Tabelle mit den C-Permanents auf den Stack legen.
|
||
lua_getfield(L, LUA_REGISTRYINDEX, PERMANENTS_TABLE_NAME);
|
||
|
||
if (!lua_isnil(L, -1))
|
||
{
|
||
// <20>ber alle Elemente der Tabelle iterieren.
|
||
lua_pushnil(L);
|
||
while (lua_next(L, -2) != 0)
|
||
{
|
||
// Wert und Index auf dem Stack duplizieren und in der Reihenfolge vertauschen.
|
||
lua_pushvalue(L, -1);
|
||
lua_pushvalue(L, -3);
|
||
|
||
// Falls geladen wird, ist der Name der Index und das Objekt der Wert.
|
||
// In diesem Fall m<>ssen also die Position von Name und Objekt auf dem Stack vertauscht werden.
|
||
if (TableType == PTT_UNPERSIST) lua_insert(L, -2);
|
||
|
||
// Eintrag in der Ergebnistabelle vornehmen.
|
||
lua_settable(L, -6);
|
||
|
||
// Wert vom Stack poppen. Der Index liegt dann oben f<>r den n<>chsten Aufruf von lua_next().
|
||
lua_pop(L, 1);
|
||
}
|
||
}
|
||
|
||
// Tabelle mit den C-Permanents vom Stack poppen.
|
||
lua_pop(L, 1);
|
||
|
||
// coroutine.yield muss extra in die Permanents-Tabelle eingetragen werden, da inaktive Coroutinen diese C-Funktion auf dem Stack liegen
|
||
// haben.
|
||
|
||
// Funktion coroutine.yield auf den Stack legen.
|
||
lua_getglobal(L, "coroutine");
|
||
lua_pushstring(L, "yield");
|
||
lua_gettable(L, -2);
|
||
|
||
// coroutine.yield mit eigenem eindeutigen Wert in der Permanents-Tabelle ablegen.
|
||
lua_pushstring(L, "coroutine.yield");
|
||
|
||
if (TableType == PTT_UNPERSIST) lua_insert(L, -2);
|
||
|
||
lua_settable(L, -4);
|
||
|
||
// Tabelle coroutine vom Stack poppen.
|
||
lua_pop(L, 1);
|
||
|
||
return true;
|
||
}
|
||
}
|
||
|
||
// -----------------------------------------------------------------------------
|
||
|
||
namespace
|
||
{
|
||
int Chunkwriter(lua_State *L, const void* p, size_t sz, void* ud)
|
||
{
|
||
vector<unsigned char> & chunkData = *reinterpret_cast<vector<unsigned char> * >(ud);
|
||
const unsigned char * buffer = reinterpret_cast<const unsigned char *>(p);
|
||
|
||
while (sz--) chunkData.push_back(*buffer++);
|
||
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
bool BS_LuaScriptEngine::Persist(BS_OutputPersistenceBlock & Writer)
|
||
{
|
||
// Den Lua-Stack leeren. pluto_persist() erwartet, dass der Stack bis aus seine Parameter leer ist.
|
||
lua_settop(m_State, 0);
|
||
|
||
// Garbage Collection erzwingen.
|
||
lua_gc(m_State, LUA_GCCOLLECT, 0);
|
||
|
||
// Permanents-Tabelle und die zu persistierende Tabelle auf den Stack legen.
|
||
// pluto_persist erwartet diese beiden Objekte auf dem Lua-Stack.
|
||
PushPermanentsTable(m_State, PTT_PERSIST);
|
||
lua_getglobal(m_State, "_G");
|
||
|
||
// Lua persistieren und die Daten in einem vector ablegen.
|
||
vector<unsigned char> chunkData;
|
||
pluto_persist(m_State, Chunkwriter, &chunkData);
|
||
|
||
// Persistenzdaten in den Writer schreiben.
|
||
Writer.Write(&chunkData[0], chunkData.size());
|
||
|
||
// Die beiden Tabellen vom Stack nehmen.
|
||
lua_pop(m_State, 2);
|
||
|
||
return true;
|
||
}
|
||
|
||
// -----------------------------------------------------------------------------
|
||
|
||
namespace
|
||
{
|
||
// -------------------------------------------------------------------------
|
||
|
||
struct ChunkreaderData
|
||
{
|
||
void * BufferPtr;
|
||
size_t Size;
|
||
bool BufferReturned;
|
||
};
|
||
|
||
// -------------------------------------------------------------------------
|
||
|
||
const char * Chunkreader(lua_State *L, void *ud, size_t *sz)
|
||
{
|
||
ChunkreaderData & cd = *reinterpret_cast<ChunkreaderData *>(ud);
|
||
|
||
if (!cd.BufferReturned)
|
||
{
|
||
cd.BufferReturned = true;
|
||
*sz = cd.Size;
|
||
return reinterpret_cast<const char *>(cd.BufferPtr);
|
||
}
|
||
else
|
||
{
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
// -------------------------------------------------------------------------
|
||
|
||
void ClearGlobalTable(lua_State * L, const char ** Exceptions)
|
||
{
|
||
// <20>ber alle Elemente der globalen Tabelle iterieren.
|
||
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
||
lua_pushnil(L);
|
||
while (lua_next(L, -2) != 0)
|
||
{
|
||
// Jetzt liegen der Wert und der Index des aktuellen Elementes auf dem Stack.
|
||
// Der Wert interessiert uns nicht, daher wird er vom Stack gepoppt.
|
||
lua_pop(L, 1);
|
||
|
||
// Feststellen, ob das Element auf nil gesetzt , also aus der globalen Tabelle entfernt werden soll.
|
||
// Hierf<72>r wird gepr<70>ft, ob das Elementname ein String ist und in der Liste der Ausnahmen vorkommt.
|
||
bool SetElementToNil = true;
|
||
if (lua_isstring(L, -1))
|
||
{
|
||
const char * IndexString = lua_tostring(L, -1);
|
||
const char ** ExceptionsWalker = Exceptions;
|
||
while (*ExceptionsWalker)
|
||
{
|
||
if (strcmp(IndexString, *ExceptionsWalker) == 0) SetElementToNil = false;
|
||
++ExceptionsWalker;
|
||
}
|
||
}
|
||
|
||
// Wenn der obige Test ergeben hat, dass das Element entfernt werden soll, wird es entfernt indem der Wert auf nil gesetzt wird.
|
||
if (SetElementToNil)
|
||
{
|
||
lua_pushvalue(L, -1);
|
||
lua_pushnil(L);
|
||
lua_settable(L, LUA_GLOBALSINDEX);
|
||
}
|
||
}
|
||
|
||
// Globale Tabelle vom Stack nehmen.
|
||
lua_pop(L, 1);
|
||
|
||
// Garbage-Collection vornehmen, damit die entfernten Elemente alle gel<65>scht werden.
|
||
lua_gc(L, LUA_GCCOLLECT, 0);
|
||
}
|
||
}
|
||
|
||
// -----------------------------------------------------------------------------
|
||
|
||
bool BS_LuaScriptEngine::Unpersist(BS_InputPersistenceBlock & Reader)
|
||
{
|
||
// Den Lua-Stack leeren. pluto_unpersist() erwartet, dass der Stack bis aus seine Parameter leer ist.
|
||
lua_settop(m_State, 0);
|
||
|
||
// Permanents Tabelle auf den Stack legen. Dies passiert schon an dieser Stelle, da zum Erstellen der Tabelle alle Permanents zugreifbar sein
|
||
// m<>ssen. Dies ist nur am Anfang dieser Funktion der Fall, im Folgenden wird die globale Tabelle geleert.
|
||
PushPermanentsTable(m_State, PTT_UNPERSIST);
|
||
|
||
// Alle Elemente aus der globalen Tabelle mit Ausnhame von _G und __METATABLES entfernen.
|
||
// Danach wird eine Garbage Collection durchgef<65>hrt und somit alle von Lua verwalteten Objekte gel<65>scht.
|
||
// __METATABLES wird zun<75>chst nicht entfernt, da die Metatables f<>r die Finalizer der Objekte ben<65>tigt werden.
|
||
static const char * ClearExceptionsFirstPass[] =
|
||
{
|
||
"_G",
|
||
"__METATABLES",
|
||
0
|
||
};
|
||
ClearGlobalTable(m_State, ClearExceptionsFirstPass);
|
||
|
||
// Im zweiten Durchgang werden auch die Metatables entfernt.
|
||
static const char * ClearExceptionsSecondPass[] =
|
||
{
|
||
"_G",
|
||
0
|
||
};
|
||
ClearGlobalTable(m_State, ClearExceptionsSecondPass);
|
||
|
||
// Persistierte Lua-Daten einlesen.
|
||
vector<unsigned char> chunkData;
|
||
Reader.Read(chunkData);
|
||
|
||
// Chunk-Reader initialisieren. Er wird von pluto_unpersist benutzt um die ben<65>tigten Daten einzulesen.
|
||
ChunkreaderData cd;
|
||
cd.BufferPtr = &chunkData[0];
|
||
cd.Size = chunkData.size();
|
||
cd.BufferReturned = false;
|
||
|
||
pluto_unpersist(m_State, Chunkreader, &cd);
|
||
|
||
// Permanents-Tabelle vom Stack nehmen.
|
||
lua_remove(m_State, -2);
|
||
|
||
// Die eingelesenen Elemente in die globale Tabelle eintragen.
|
||
lua_pushnil(m_State);
|
||
while (lua_next(m_State, -2) != 0)
|
||
{
|
||
// Die Referenz auf die globale Tabelle (_G) darf nicht <20>berschrieben werden, sonst tickt Lua total aus.
|
||
bool IsGlobalReference = lua_isstring(m_State, -2) && strcmp(lua_tostring(m_State, -2), "_G") == 0;
|
||
if (!IsGlobalReference)
|
||
{
|
||
lua_pushvalue(m_State, -2);
|
||
lua_pushvalue(m_State, -2);
|
||
|
||
lua_settable(m_State, LUA_GLOBALSINDEX);
|
||
}
|
||
|
||
// Wert vom Stack poppen. Der Index liegt dann oben f<>r den n<>chsten Aufruf von lua_next().
|
||
lua_pop(m_State, 1);
|
||
}
|
||
|
||
// Tabelle mit den geladenen Daten vom Stack poppen.
|
||
lua_pop(m_State, 1);
|
||
|
||
// Garbage Collection erzwingen.
|
||
lua_gc(m_State, LUA_GCCOLLECT, 0);
|
||
|
||
return true;
|
||
}
|