mirror of
https://github.com/edubart/otclient.git
synced 2025-05-07 04:49:21 +02:00

lua_newtable() definition: #define lua_newtable(L) lua_createtable(L, 0, 0) This simply allocates space for 0 fields, meaning, if we push any values into this array, it will re-allocate the array, which is bad. This function statically allocates an array, so it's ready to have X fields into it. Performance tests: lua_newtable 1428161 1426992 1413513 lua_createtable 2004544 1974117 1957533 These tests were done on an AMD 8350fx CPU, single thread used. narr: This is for fields that just have an index, e.g. arr[0] etc. nrec: For fields which needs like arr.a, arr.b etc. This is how many times each of the functions can run per second, as you can see about 1.7x the calls to lua_newtable. All credits goes to @dalkon, he was too lazy to do it by himself, and asked me to do it for him.
1280 lines
31 KiB
C++
1280 lines
31 KiB
C++
/*
|
|
* Copyright (c) 2010-2013 OTClient <https://github.com/edubart/otclient>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
#include "luainterface.h"
|
|
#include "luaobject.h"
|
|
|
|
#include <framework/core/resourcemanager.h>
|
|
#include <lua.hpp>
|
|
|
|
#include "lbitlib.h"
|
|
|
|
LuaInterface g_lua;
|
|
|
|
LuaInterface::LuaInterface()
|
|
{
|
|
L = nullptr;
|
|
m_cppCallbackDepth = 0;
|
|
m_weakTableRef = 0;
|
|
m_totalObjRefs = 0;
|
|
m_totalFuncRefs = 0;
|
|
}
|
|
|
|
LuaInterface::~LuaInterface()
|
|
{
|
|
}
|
|
|
|
void LuaInterface::init()
|
|
{
|
|
createLuaState();
|
|
|
|
// store global environment reference
|
|
pushThread();
|
|
getEnv();
|
|
m_globalEnv = ref();
|
|
pop();
|
|
|
|
// check if demangle_class is working as expected
|
|
assert(stdext::demangle_class<LuaObject>() == "LuaObject");
|
|
|
|
// register LuaObject, the base of all other objects
|
|
registerClass<LuaObject>();
|
|
bindClassMemberFunction<LuaObject>("getUseCount", &LuaObject::getUseCount);
|
|
bindClassMemberFunction<LuaObject>("getClassName", &LuaObject::getClassName);
|
|
|
|
registerClassMemberFunction<LuaObject>("getFieldsTable", (LuaCppFunction) ([](LuaInterface* lua) -> int {
|
|
LuaObjectPtr obj = g_lua.popObject();
|
|
obj->luaGetFieldsTable();
|
|
return 1;
|
|
}));
|
|
}
|
|
|
|
void LuaInterface::terminate()
|
|
{
|
|
// close lua state, it will release all objects
|
|
closeLuaState();
|
|
assert(m_totalFuncRefs == 0);
|
|
assert(m_totalObjRefs == 0);
|
|
}
|
|
|
|
void LuaInterface::registerSingletonClass(const std::string& className)
|
|
{
|
|
newTable();
|
|
pushValue();
|
|
setGlobal(className);
|
|
pop();
|
|
}
|
|
|
|
void LuaInterface::registerClass(const std::string& className, const std::string& baseClass)
|
|
{
|
|
// creates the class table (that it's also the class methods table)
|
|
newTable();
|
|
pushValue();
|
|
setGlobal(className);
|
|
const int klass = getTop();
|
|
|
|
// creates the class fieldmethods table
|
|
newTable();
|
|
pushValue();
|
|
setGlobal(className + "_fieldmethods");
|
|
int klass_fieldmethods = getTop();
|
|
|
|
// creates the class metatable
|
|
newTable();
|
|
pushValue();
|
|
setGlobal(className + "_mt");
|
|
int klass_mt = getTop();
|
|
|
|
// set metatable metamethods
|
|
pushCppFunction(&LuaInterface::luaObjectGetEvent);
|
|
setField("__index", klass_mt);
|
|
pushCppFunction(&LuaInterface::luaObjectSetEvent);
|
|
setField("__newindex", klass_mt);
|
|
pushCppFunction(&LuaInterface::luaObjectEqualEvent);
|
|
setField("__eq", klass_mt);
|
|
pushCppFunction(&LuaInterface::luaObjectCollectEvent);
|
|
setField("__gc", klass_mt);
|
|
|
|
// set some fields that will be used later in metatable
|
|
pushValue(klass);
|
|
setField("methods", klass_mt);
|
|
pushValue(klass_fieldmethods);
|
|
setField("fieldmethods", klass_mt);
|
|
|
|
// redirect methods and fieldmethods to the base class ones
|
|
if(!className.empty() && className != "LuaObject") {
|
|
// the following code is what create classes hierarchy for lua, by reproducing:
|
|
// DerivedClass = { __index = BaseClass }
|
|
// DerivedClass_fieldmethods = { __index = BaseClass_methods }
|
|
|
|
// redirect the class methods to the base methods
|
|
pushValue(klass);
|
|
newTable();
|
|
getGlobal(baseClass);
|
|
setField("__index");
|
|
setMetatable();
|
|
pop();
|
|
|
|
// redirect the class fieldmethods to the base fieldmethods
|
|
pushValue(klass_fieldmethods);
|
|
newTable();
|
|
getGlobal(baseClass + "_fieldmethods");
|
|
setField("__index");
|
|
setMetatable();
|
|
pop();
|
|
}
|
|
|
|
// pops klass, klass_mt, klass_fieldmethods
|
|
pop(3);
|
|
}
|
|
|
|
void LuaInterface::registerClassStaticFunction(const std::string& className,
|
|
const std::string& functionName,
|
|
const LuaCppFunction& function)
|
|
{
|
|
registerClassMemberFunction(className, functionName, function);
|
|
}
|
|
|
|
void LuaInterface::registerClassMemberFunction(const std::string& className,
|
|
const std::string& functionName,
|
|
const LuaCppFunction& function)
|
|
{
|
|
getGlobal(className);
|
|
pushCppFunction(function);
|
|
setField(functionName);
|
|
pop();
|
|
}
|
|
|
|
void LuaInterface::registerClassMemberField(const std::string& className,
|
|
const std::string& field,
|
|
const LuaCppFunction& getFunction,
|
|
const LuaCppFunction& setFunction)
|
|
{
|
|
getGlobal(className + "_fieldmethods");
|
|
|
|
if(getFunction) {
|
|
pushCppFunction(getFunction);
|
|
setField(stdext::format("get_%s", field));
|
|
}
|
|
|
|
if(setFunction) {
|
|
pushCppFunction(setFunction);
|
|
setField(stdext::format("set_%s", field));
|
|
}
|
|
|
|
pop();
|
|
}
|
|
|
|
void LuaInterface::registerGlobalFunction(const std::string& functionName, const LuaCppFunction& function)
|
|
{
|
|
pushCppFunction(function);
|
|
setGlobal(functionName);
|
|
}
|
|
|
|
int LuaInterface::luaObjectGetEvent(LuaInterface* lua)
|
|
{
|
|
// stack: obj, key
|
|
LuaObjectPtr obj = lua->toObject(-2);
|
|
std::string key = lua->toString(-1);
|
|
assert(obj);
|
|
|
|
lua->remove(-1); // removes key
|
|
|
|
// if a get method for this key exists, calls it
|
|
lua->getMetatable(); // pushes obj metatable
|
|
lua->getField("fieldmethods"); // push obj fieldmethods
|
|
lua->remove(-2); // removes obj metatable
|
|
lua->getField("get_" + key); // pushes get method
|
|
lua->remove(-2); // remove obj fieldmethods
|
|
if(!lua->isNil()) { // is the get method not nil?
|
|
lua->insert(-2); // moves obj to the top
|
|
lua->signalCall(1, 1); // calls get method, arguments: obj
|
|
return 1;
|
|
}
|
|
lua->pop(); // pops the nil get method
|
|
|
|
// if the field for this key exists, returns it
|
|
obj->luaGetField(key);
|
|
if(!lua->isNil()) {
|
|
lua->remove(-2); // removes the obj
|
|
// field value is on the stack
|
|
return 1;
|
|
}
|
|
lua->pop(); // pops the nil field
|
|
|
|
// pushes the method assigned by this key
|
|
lua->getMetatable(); // pushes obj metatable
|
|
lua->getField("methods"); // push obj methods
|
|
lua->remove(-2); // removes obj metatable
|
|
lua->getField(key); // pushes obj method
|
|
lua->remove(-2); // remove obj methods
|
|
lua->remove(-2); // removes obj
|
|
|
|
// the result value is on the stack
|
|
return 1;
|
|
}
|
|
|
|
int LuaInterface::luaObjectSetEvent(LuaInterface* lua)
|
|
{
|
|
// stack: obj, key, value
|
|
LuaObjectPtr obj = lua->toObject(-3);
|
|
std::string key = lua->toString(-2);
|
|
assert(obj);
|
|
|
|
lua->remove(-2); // removes key
|
|
lua->insert(-2); // moves obj to the top
|
|
|
|
// check if a set method for this field exists and call it
|
|
lua->getMetatable(); // pushes obj metatable
|
|
lua->getField("fieldmethods"); // push obj fieldmethods
|
|
lua->remove(-2); // removes obj metatable
|
|
lua->getField("set_" + key); // pushes set method
|
|
lua->remove(-2); // remove obj fieldmethods
|
|
if(!lua->isNil()) { // is the set method not nil?
|
|
lua->insert(-3); // moves func to -3
|
|
lua->insert(-2); // moves obj to -2, and value to -1
|
|
lua->signalCall(2, 0); // calls set method, arguments: obj, value
|
|
return 0;
|
|
}
|
|
lua->pop(); // pops the nil set method
|
|
|
|
// no set method exists, then treats as an field and set it
|
|
lua->pop(); // pops the object
|
|
obj->luaSetField(key); // sets the obj field
|
|
return 0;
|
|
}
|
|
|
|
int LuaInterface::luaObjectEqualEvent(LuaInterface* lua)
|
|
{
|
|
// stack: obj1, obj2
|
|
bool ret = false;
|
|
|
|
// check if obj1 == obj2
|
|
if(lua->isUserdata(-1) && lua->isUserdata(-2)) {
|
|
LuaObjectPtr* objPtr2 = static_cast<LuaObjectPtr*>(lua->popUserdata());
|
|
LuaObjectPtr* objPtr1 = static_cast<LuaObjectPtr*>(lua->popUserdata());
|
|
assert(objPtr1 && objPtr2);
|
|
if(*objPtr1 == *objPtr2)
|
|
ret = true;
|
|
} else
|
|
lua->pop(2);
|
|
|
|
lua->pushBoolean(ret);
|
|
return 1;
|
|
}
|
|
|
|
int LuaInterface::luaObjectCollectEvent(LuaInterface* lua)
|
|
{
|
|
// gets object pointer
|
|
auto objPtr = static_cast<LuaObjectPtr*>(lua->popUserdata());
|
|
assert(objPtr);
|
|
|
|
// resets pointer to decrease object use count
|
|
objPtr->reset();
|
|
g_lua.m_totalObjRefs--;
|
|
return 0;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
bool LuaInterface::safeRunScript(const std::string& fileName)
|
|
{
|
|
try {
|
|
runScript(fileName);
|
|
return true;
|
|
} catch(stdext::exception& e) {
|
|
g_logger.error(stdext::format("Failed to load script '%s': %s", fileName, e.what()));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void LuaInterface::runScript(const std::string& fileName)
|
|
{
|
|
loadScript(fileName);
|
|
safeCall(0, 0);
|
|
}
|
|
|
|
void LuaInterface::runBuffer(const std::string& buffer, const std::string& source)
|
|
{
|
|
loadBuffer(buffer, source);
|
|
safeCall(0, 0);
|
|
}
|
|
|
|
void LuaInterface::loadScript(const std::string& fileName)
|
|
{
|
|
// resolve file full path
|
|
std::string filePath = fileName;
|
|
if(!stdext::starts_with(fileName, "/"))
|
|
filePath = getCurrentSourcePath() + "/" + filePath;
|
|
|
|
filePath = g_resources.guessFilePath(filePath, "lua");
|
|
|
|
std::string buffer = g_resources.readFileContents(filePath);
|
|
std::string source = "@" + filePath;
|
|
loadBuffer(buffer, source);
|
|
}
|
|
|
|
void LuaInterface::loadFunction(const std::string& buffer, const std::string& source)
|
|
{
|
|
if(buffer.empty()) {
|
|
pushNil();
|
|
return;
|
|
}
|
|
|
|
std::string buf;
|
|
if(stdext::starts_with(buffer, "function"))
|
|
buf = stdext::format("__func = %s", buffer);
|
|
else
|
|
buf = stdext::format("__func = function(self)\n%s\nend", buffer);
|
|
|
|
loadBuffer(buf, source);
|
|
safeCall();
|
|
|
|
// get the function
|
|
getGlobal("__func");
|
|
|
|
// reset the global __func
|
|
pushNil();
|
|
setGlobal("__func");
|
|
}
|
|
|
|
void LuaInterface::evaluateExpression(const std::string& expression, const std::string& source)
|
|
{
|
|
// evaluates the expression
|
|
if(!expression.empty()) {
|
|
std::string buffer = stdext::format("__exp = (%s)", expression);
|
|
loadBuffer(buffer, source);
|
|
safeCall();
|
|
|
|
// gets the expression result
|
|
getGlobal("__exp");
|
|
|
|
// resets global __exp
|
|
pushNil();
|
|
setGlobal("__exp");
|
|
} else
|
|
pushNil();
|
|
}
|
|
|
|
std::string LuaInterface::traceback(const std::string& errorMessage, int level)
|
|
{
|
|
// gets debug.traceback
|
|
getGlobal("debug");
|
|
getField("traceback");
|
|
remove(-2); // remove debug
|
|
|
|
// calls debug.traceback(errorMessage, level)
|
|
pushString(errorMessage);
|
|
pushInteger(level);
|
|
call(2,1);
|
|
|
|
// returns the traceback message
|
|
return popString();
|
|
}
|
|
|
|
void LuaInterface::throwError(const std::string& message)
|
|
{
|
|
if(isInCppCallback()) {
|
|
pushString(message);
|
|
error();
|
|
} else
|
|
throw stdext::exception(message);
|
|
}
|
|
|
|
std::string LuaInterface::getCurrentSourcePath(int level)
|
|
{
|
|
std::string path;
|
|
if(!L)
|
|
return path;
|
|
|
|
// check all stack functions for script source path
|
|
while(true) {
|
|
getStackFunction(level); // pushes stack function
|
|
|
|
// only lua functions is wanted, because only them have a working directory
|
|
if(isLuaFunction()) {
|
|
path = functionSourcePath();
|
|
break;
|
|
} else if(isNil()) {
|
|
pop();
|
|
break;
|
|
} else
|
|
pop();
|
|
|
|
// next level
|
|
level++;
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
int LuaInterface::safeCall(int numArgs, int numRets)
|
|
{
|
|
assert(hasIndex(-numArgs-1));
|
|
|
|
// saves the current stack size for calculating the number of results later
|
|
int previousStackSize = stackSize();
|
|
|
|
// pushes error function
|
|
int errorFuncIndex = previousStackSize - numArgs;
|
|
pushCFunction(&LuaInterface::luaErrorHandler);
|
|
insert(errorFuncIndex);
|
|
|
|
// calls the function in protected mode (means errors will be caught)
|
|
int ret = pcall(numArgs, LUA_MULTRET, errorFuncIndex);
|
|
|
|
remove(errorFuncIndex); // remove error func
|
|
|
|
// if there was an error throw an exception
|
|
if(ret != 0)
|
|
throw LuaException(popString());
|
|
|
|
int rets = (stackSize() + numArgs + 1) - previousStackSize;
|
|
while(numRets != -1 && rets != numRets) {
|
|
if(rets < numRets) {
|
|
pushNil();
|
|
rets++;
|
|
} else {
|
|
pop();
|
|
rets--;
|
|
}
|
|
}
|
|
|
|
// returns the number of results
|
|
return rets;
|
|
}
|
|
|
|
int LuaInterface::signalCall(int numArgs, int numRets)
|
|
{
|
|
int rets = 0;
|
|
int funcIndex = -numArgs-1;
|
|
|
|
try {
|
|
// must be a function
|
|
if(isFunction(funcIndex)) {
|
|
rets = safeCall(numArgs);
|
|
|
|
if(numRets != -1) {
|
|
if(rets != numRets)
|
|
throw LuaException("function call didn't return the expected number of results", 0);
|
|
}
|
|
}
|
|
// can also calls table of functions
|
|
else if(isTable(funcIndex)) {
|
|
// loop through table values
|
|
pushNil();
|
|
bool done = false;
|
|
while(next(funcIndex-1)) {
|
|
if(isFunction()) {
|
|
// repush arguments
|
|
for(int i=0;i<numArgs;++i)
|
|
pushValue(-numArgs-2);
|
|
|
|
int rets = safeCall(numArgs);
|
|
if(rets == 1) {
|
|
done = popBoolean();
|
|
if(done) {
|
|
pop();
|
|
break;
|
|
}
|
|
} else if(rets != 0)
|
|
throw LuaException("function call didn't return the expected number of results", 0);
|
|
} else {
|
|
throw LuaException("attempt to call a non function", 0);
|
|
}
|
|
}
|
|
pop(numArgs + 1); // pops the table of function and arguments
|
|
|
|
if(numRets == 1 || numRets == -1) {
|
|
rets = 1;
|
|
pushBoolean(done);
|
|
}
|
|
}
|
|
// nil values are ignored
|
|
else if(isNil(funcIndex)) {
|
|
pop(numArgs + 1); // pops the function and arguments
|
|
}
|
|
// if not nil, warn
|
|
else {
|
|
throw LuaException("attempt to call a non function value", 0);
|
|
}
|
|
} catch(stdext::exception& e) {
|
|
g_logger.error(stdext::format("protected lua call failed: %s", e.what()));
|
|
}
|
|
|
|
// pushes nil values if needed
|
|
while(numRets != -1 && rets < numRets) {
|
|
pushNil();
|
|
rets++;
|
|
}
|
|
|
|
// returns the number of results on the stack
|
|
return rets;
|
|
}
|
|
|
|
int LuaInterface::newSandboxEnv()
|
|
{
|
|
newTable(); // pushes the new environment table
|
|
newTable(); // pushes the new environment metatable
|
|
getRef(getGlobalEnvironment()); // pushes the global environment
|
|
setField("__index"); // sets metatable __index to the global environment
|
|
setMetatable(); // assigns environment metatable
|
|
return ref(); // return a reference to the environment table
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// lua C functions
|
|
|
|
int LuaInterface::luaScriptLoader(lua_State* L)
|
|
{
|
|
// loads the script as a function
|
|
std::string fileName = g_lua.popString();
|
|
|
|
try {
|
|
g_lua.loadScript(fileName);
|
|
return 1;
|
|
} catch(stdext::exception& e) {
|
|
g_lua.pushString(std::string("\n\t") + e.what());
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
int LuaInterface::lua_dofile(lua_State* L)
|
|
{
|
|
std::string file = g_lua.popString();
|
|
|
|
try {
|
|
g_lua.loadScript(file);
|
|
g_lua.call(0, LUA_MULTRET);
|
|
return g_lua.stackSize();
|
|
} catch(stdext::exception& e) {
|
|
g_lua.pushString(e.what());
|
|
g_lua.error();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int LuaInterface::lua_dofiles(lua_State* L)
|
|
{
|
|
std::string contains = "";
|
|
if(g_lua.getTop() > 2) {
|
|
contains = g_lua.popString();
|
|
}
|
|
|
|
bool recursive = false;
|
|
if(g_lua.getTop() > 1) {
|
|
recursive = g_lua.popBoolean();
|
|
}
|
|
|
|
std::string directory = g_lua.popString();
|
|
g_lua.loadFiles(directory, recursive, contains);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int LuaInterface::lua_loadfile(lua_State* L)
|
|
{
|
|
std::string fileName = g_lua.popString();
|
|
|
|
try {
|
|
g_lua.loadScript(fileName);
|
|
return 1;
|
|
} catch(stdext::exception& e) {
|
|
g_lua.pushNil();
|
|
g_lua.pushString(e.what());
|
|
g_lua.error();
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
int LuaInterface::luaErrorHandler(lua_State* L)
|
|
{
|
|
// pops the error message
|
|
auto error = g_lua.popString();
|
|
|
|
// prevents repeated tracebacks
|
|
if(error.find("stack traceback:") == std::string::npos)
|
|
error = g_lua.traceback(error, 1);
|
|
|
|
// pushes the new error message with traceback information
|
|
g_lua.pushString(error);
|
|
return 1;
|
|
}
|
|
|
|
int LuaInterface::luaCppFunctionCallback(lua_State* L)
|
|
{
|
|
// retrieves function pointer from userdata
|
|
auto funcPtr = static_cast<LuaCppFunctionPtr*>(g_lua.popUpvalueUserdata());
|
|
assert(funcPtr);
|
|
|
|
int numRets = 0;
|
|
|
|
// do the call
|
|
try {
|
|
g_lua.m_cppCallbackDepth++;
|
|
numRets = (*(funcPtr->get()))(&g_lua);
|
|
g_lua.m_cppCallbackDepth--;
|
|
assert(numRets == g_lua.stackSize());
|
|
} catch(stdext::exception& e) {
|
|
// cleanup stack
|
|
while(g_lua.stackSize() > 0)
|
|
g_lua.pop();
|
|
numRets = 0;
|
|
g_lua.pushString(stdext::format("C++ call failed: %s", g_lua.traceback(e.what())));
|
|
g_lua.error();
|
|
}
|
|
|
|
return numRets;
|
|
}
|
|
|
|
int LuaInterface::luaCollectCppFunction(lua_State* L)
|
|
{
|
|
auto funcPtr = static_cast<LuaCppFunctionPtr*>(g_lua.popUserdata());
|
|
assert(funcPtr);
|
|
funcPtr->reset();
|
|
g_lua.m_totalFuncRefs--;
|
|
return 0;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// from here all next functions are interfaces for the Lua API
|
|
|
|
void LuaInterface::createLuaState()
|
|
{
|
|
// creates lua state
|
|
L = luaL_newstate();
|
|
if(!L)
|
|
g_logger.fatal("Unable to create lua state");
|
|
|
|
// load lua standard libraries
|
|
luaL_openlibs(L);
|
|
|
|
// load bit32 lib for bitwise operations
|
|
luaopen_bit32(L);
|
|
|
|
// creates weak table
|
|
newTable();
|
|
newTable();
|
|
pushString("v");
|
|
setField("__mode");
|
|
setMetatable();
|
|
m_weakTableRef = ref();
|
|
|
|
// installs script loader
|
|
getGlobal("package");
|
|
getField("loaders");
|
|
pushCFunction(&LuaInterface::luaScriptLoader);
|
|
rawSeti(5);
|
|
pop(2);
|
|
|
|
// replace dofile
|
|
pushCFunction(&LuaInterface::lua_dofile);
|
|
setGlobal("dofile");
|
|
|
|
// dofiles
|
|
pushCFunction(&LuaInterface::lua_dofiles);
|
|
setGlobal("dofiles");
|
|
|
|
// replace loadfile
|
|
pushCFunction(&LuaInterface::lua_loadfile);
|
|
setGlobal("loadfile");
|
|
}
|
|
|
|
void LuaInterface::closeLuaState()
|
|
{
|
|
if(L) {
|
|
// close lua, it also collects
|
|
lua_close(L);
|
|
L = NULL;
|
|
}
|
|
}
|
|
|
|
void LuaInterface::collectGarbage()
|
|
{
|
|
// prevents recursive collects
|
|
static bool collecting = false;
|
|
if(!collecting) {
|
|
collecting = true;
|
|
|
|
// we must collect two times because __gc metamethod
|
|
// is called on uservalues only the second time
|
|
for(int i=0;i<2;++i)
|
|
lua_gc(L, LUA_GCCOLLECT, 0);
|
|
|
|
collecting = false;
|
|
}
|
|
}
|
|
|
|
void LuaInterface::loadBuffer(const std::string& buffer, const std::string& source)
|
|
{
|
|
// loads lua buffer
|
|
int ret = luaL_loadbuffer(L, buffer.c_str(), buffer.length(), source.c_str());
|
|
if(ret != 0)
|
|
throw LuaException(popString(), 0);
|
|
}
|
|
|
|
int LuaInterface::pcall(int numArgs, int numRets, int errorFuncIndex)
|
|
{
|
|
assert(hasIndex(-numArgs - 1));
|
|
return lua_pcall(L, numArgs, numRets, errorFuncIndex);
|
|
}
|
|
|
|
void LuaInterface::call(int numArgs, int numRets)
|
|
{
|
|
assert(hasIndex(-numArgs - 1));
|
|
lua_call(L, numArgs, numRets);
|
|
}
|
|
|
|
void LuaInterface::error()
|
|
{
|
|
assert(hasIndex(-1));
|
|
lua_error(L);
|
|
}
|
|
|
|
int LuaInterface::ref()
|
|
{
|
|
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
assert(ref != LUA_NOREF);
|
|
assert(ref < 2147483647);
|
|
return ref;
|
|
}
|
|
|
|
int LuaInterface::weakRef()
|
|
{
|
|
static int id = 0;
|
|
|
|
// generates a new id
|
|
++id;
|
|
if(id == 2147483647)
|
|
id = 0;
|
|
|
|
// gets weak table
|
|
getRef(m_weakTableRef);
|
|
insert(-2);
|
|
|
|
// sets weak_table[id] = v
|
|
rawSeti(id);
|
|
|
|
// pops weak table
|
|
pop();
|
|
|
|
return id;
|
|
}
|
|
|
|
void LuaInterface::unref(int ref)
|
|
{
|
|
if(ref >= 0 && L != NULL)
|
|
luaL_unref(L, LUA_REGISTRYINDEX, ref);
|
|
}
|
|
|
|
const char* LuaInterface::typeName(int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
int type = lua_type(L, index);
|
|
return lua_typename(L, type);
|
|
}
|
|
|
|
std::string LuaInterface::functionSourcePath()
|
|
{
|
|
std::string path;
|
|
|
|
// gets function source path
|
|
lua_Debug ar;
|
|
memset(&ar, 0, sizeof(ar));
|
|
lua_getinfo(L, ">Sn", &ar);
|
|
if(ar.source) {
|
|
// scripts coming from files has source beginning with '@'
|
|
if(ar.source[0] == '@') {
|
|
path = ar.source;
|
|
path = path.substr(1, path.find_last_of("/") - 1);
|
|
path = path.substr(0, path.find_last_of(":"));
|
|
}
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
void LuaInterface::insert(int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
lua_insert(L, index);
|
|
}
|
|
|
|
void LuaInterface::remove(int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
lua_remove(L, index);
|
|
}
|
|
|
|
bool LuaInterface::next(int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
return lua_next(L, index);
|
|
}
|
|
|
|
void LuaInterface::getStackFunction(int level)
|
|
{
|
|
lua_Debug ar;
|
|
if(lua_getstack(L, level, &ar) == 1)
|
|
lua_getinfo(L, "f", &ar);
|
|
else
|
|
pushNil();
|
|
}
|
|
|
|
void LuaInterface::getRef(int ref)
|
|
{
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
|
|
}
|
|
|
|
void LuaInterface::getWeakRef(int weakRef)
|
|
{
|
|
// pushes weak_table[weakRef]
|
|
getRef(m_weakTableRef);
|
|
rawGeti(weakRef);
|
|
remove(-2);
|
|
}
|
|
|
|
void LuaInterface::setGlobalEnvironment(int env)
|
|
{
|
|
pushThread();
|
|
getRef(env);
|
|
assert(isTable());
|
|
setEnv();
|
|
pop();
|
|
}
|
|
|
|
void LuaInterface::setMetatable(int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
lua_setmetatable(L, index);
|
|
}
|
|
|
|
void LuaInterface::getMetatable(int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
lua_getmetatable(L, index);
|
|
}
|
|
|
|
void LuaInterface::getField(const char* key, int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
assert(isUserdata(index) || isTable(index));
|
|
lua_getfield(L, index, key);
|
|
}
|
|
|
|
void LuaInterface::setField(const char* key, int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
assert(isUserdata(index) || isTable(index));
|
|
lua_setfield(L, index, key);
|
|
}
|
|
|
|
void LuaInterface::getEnv(int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
lua_getfenv(L, index);
|
|
}
|
|
|
|
void LuaInterface::setEnv(int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
lua_setfenv(L, index);
|
|
}
|
|
|
|
void LuaInterface::getTable(int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
lua_gettable(L, index);
|
|
}
|
|
|
|
void LuaInterface::setTable(int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
lua_settable(L, index);
|
|
}
|
|
|
|
void LuaInterface::clearTable(int index)
|
|
{
|
|
assert(hasIndex(index) && isTable(index));
|
|
pushNil(); // table, nil
|
|
bool stop = false;
|
|
while(next(index-1)) { // table, key, value
|
|
pop(); // table, key
|
|
pushValue(); // table, key, key
|
|
if(next(index-2)) { // table, key, nextkey, value
|
|
pop(); // table, key, nextkey
|
|
insert(-2); // table, nextkey, key
|
|
pushNil(); // table, nextkey, key, nil
|
|
rawSet(index-3); // table, nextkey
|
|
} else { // table, key
|
|
pushNil(); // table, key, nil
|
|
rawSet(index-2); // table
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void LuaInterface::getGlobal(const std::string& key)
|
|
{
|
|
lua_getglobal(L, key.c_str());
|
|
}
|
|
|
|
void LuaInterface::getGlobalField(const std::string& globalKey, const std::string& fieldKey)
|
|
{
|
|
getGlobal(globalKey);
|
|
if(!isNil()) {
|
|
assert(isTable() || isUserdata());
|
|
getField(fieldKey);
|
|
remove(-2);
|
|
}
|
|
}
|
|
|
|
void LuaInterface::setGlobal(const std::string& key)
|
|
{
|
|
assert(hasIndex(-1));
|
|
lua_setglobal(L, key.c_str());
|
|
}
|
|
|
|
void LuaInterface::rawGet(int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
lua_rawget(L, index);
|
|
}
|
|
|
|
void LuaInterface::rawGeti(int n, int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
lua_rawgeti(L, index, n);
|
|
}
|
|
|
|
void LuaInterface::rawSet(int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
lua_rawset(L, index);
|
|
}
|
|
|
|
void LuaInterface::rawSeti(int n, int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
lua_rawseti(L, index, n);
|
|
}
|
|
|
|
void LuaInterface::newTable()
|
|
{
|
|
lua_newtable(L);
|
|
}
|
|
|
|
void LuaInterface::createTable(int narr, int nrec)
|
|
{
|
|
lua_createtable(L, narr, nrec);
|
|
}
|
|
|
|
void* LuaInterface::newUserdata(int size)
|
|
{
|
|
return lua_newuserdata(L, size);
|
|
}
|
|
|
|
void LuaInterface::pop(int n)
|
|
{
|
|
if(n > 0) {
|
|
assert(hasIndex(-n));
|
|
lua_pop(L, n);
|
|
}
|
|
}
|
|
|
|
long LuaInterface::popInteger()
|
|
{
|
|
assert(hasIndex(-1));
|
|
long v = toInteger(-1);
|
|
pop();
|
|
return v;
|
|
}
|
|
|
|
double LuaInterface::popNumber()
|
|
{
|
|
assert(hasIndex(-1));
|
|
double v = toNumber(-1);
|
|
pop();
|
|
return v;
|
|
}
|
|
|
|
bool LuaInterface::popBoolean()
|
|
{
|
|
assert(hasIndex(-1));
|
|
bool v = toBoolean(-1);
|
|
pop();
|
|
return v;
|
|
}
|
|
|
|
std::string LuaInterface::popString()
|
|
{
|
|
assert(hasIndex(-1));
|
|
std::string v = toString(-1);
|
|
pop();
|
|
return v;
|
|
}
|
|
|
|
void* LuaInterface::popUserdata()
|
|
{
|
|
assert(hasIndex(-1));
|
|
void* v = toUserdata(-1);
|
|
pop();
|
|
return v;
|
|
}
|
|
|
|
LuaObjectPtr LuaInterface::popObject()
|
|
{
|
|
assert(hasIndex(-1));
|
|
LuaObjectPtr v = toObject(-1);
|
|
pop();
|
|
return v;
|
|
}
|
|
|
|
void* LuaInterface::popUpvalueUserdata()
|
|
{
|
|
return lua_touserdata(L, lua_upvalueindex(1));
|
|
}
|
|
|
|
void LuaInterface::pushNil()
|
|
{
|
|
lua_pushnil(L);
|
|
checkStack();
|
|
}
|
|
|
|
void LuaInterface::pushInteger(long v)
|
|
{
|
|
lua_pushinteger(L, v);
|
|
checkStack();
|
|
}
|
|
|
|
void LuaInterface::pushNumber(double v)
|
|
{
|
|
lua_pushnumber(L, v);
|
|
checkStack();
|
|
}
|
|
|
|
void LuaInterface::pushBoolean(bool v)
|
|
{
|
|
lua_pushboolean(L, v);
|
|
checkStack();
|
|
}
|
|
|
|
void LuaInterface::pushCString(const char* v)
|
|
{
|
|
assert(v);
|
|
lua_pushstring(L, v);
|
|
checkStack();
|
|
}
|
|
|
|
void LuaInterface::pushString(const std::string& v)
|
|
{
|
|
lua_pushlstring(L, v.c_str(), v.length());
|
|
checkStack();
|
|
}
|
|
|
|
void LuaInterface::pushLightUserdata(void* p)
|
|
{
|
|
lua_pushlightuserdata(L, p);
|
|
checkStack();
|
|
}
|
|
|
|
void LuaInterface::pushThread()
|
|
{
|
|
lua_pushthread(L);
|
|
checkStack();
|
|
}
|
|
|
|
void LuaInterface::pushObject(const LuaObjectPtr& obj)
|
|
{
|
|
// fills a new userdata with a new LuaObjectPtr pointer
|
|
new(newUserdata(sizeof(LuaObjectPtr))) LuaObjectPtr(obj);
|
|
m_totalObjRefs++;
|
|
|
|
obj->luaGetMetatable();
|
|
if(isNil())
|
|
g_logger.fatal(stdext::format("metatable for class '%s' not found, did you bind the C++ class?", obj->getClassName()));
|
|
setMetatable();
|
|
}
|
|
|
|
void LuaInterface::pushCFunction(LuaCFunction func, int n)
|
|
{
|
|
lua_pushcclosure(L, func, n);
|
|
checkStack();
|
|
}
|
|
|
|
void LuaInterface::pushCppFunction(const LuaCppFunction& func)
|
|
{
|
|
// create a pointer to func (this pointer will hold the function existence)
|
|
new(newUserdata(sizeof(LuaCppFunctionPtr))) LuaCppFunctionPtr(new LuaCppFunction(func));
|
|
m_totalFuncRefs++;
|
|
|
|
// sets the userdata __gc metamethod, needed to free the function pointer when it gets collected
|
|
newTable();
|
|
pushCFunction(&LuaInterface::luaCollectCppFunction);
|
|
setField("__gc");
|
|
setMetatable();
|
|
|
|
// actually pushes a C function callback that will call the cpp function
|
|
pushCFunction(&LuaInterface::luaCppFunctionCallback, 1);
|
|
}
|
|
|
|
void LuaInterface::pushValue(int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
lua_pushvalue(L, index);
|
|
checkStack();
|
|
}
|
|
|
|
bool LuaInterface::isNil(int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
return lua_isnil(L, index);
|
|
}
|
|
|
|
bool LuaInterface::isBoolean(int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
return lua_isboolean(L, index);
|
|
}
|
|
|
|
bool LuaInterface::isNumber(int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
return lua_isnumber(L, index);
|
|
}
|
|
|
|
bool LuaInterface::isString(int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
return lua_isstring(L, index);
|
|
}
|
|
|
|
bool LuaInterface::isTable(int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
return lua_istable(L, index);
|
|
}
|
|
|
|
bool LuaInterface::isFunction(int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
return lua_isfunction(L, index);
|
|
}
|
|
|
|
bool LuaInterface::isCFunction(int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
return lua_iscfunction(L, index);
|
|
}
|
|
|
|
bool LuaInterface::isUserdata(int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
return lua_isuserdata(L, index);
|
|
}
|
|
|
|
bool LuaInterface::toBoolean(int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
return (bool)lua_toboolean(L, index);
|
|
}
|
|
|
|
int LuaInterface::toInteger(int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
return lua_tointeger(L, index);
|
|
}
|
|
|
|
double LuaInterface::toNumber(int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
return lua_tonumber(L, index);
|
|
}
|
|
|
|
const char* LuaInterface::toCString(int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
return lua_tostring(L, index);
|
|
}
|
|
|
|
std::string LuaInterface::toString(int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
std::string str;
|
|
size_t len;
|
|
const char *c_str = lua_tolstring(L, index, &len);
|
|
if(c_str && len > 0)
|
|
str.assign(c_str, len);
|
|
return str;
|
|
}
|
|
|
|
void* LuaInterface::toUserdata(int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
return lua_touserdata(L, index);
|
|
}
|
|
|
|
LuaObjectPtr LuaInterface::toObject(int index)
|
|
{
|
|
assert(hasIndex(index));
|
|
if(isUserdata(index)) {
|
|
LuaObjectPtr* objRef = static_cast<LuaObjectPtr*>(toUserdata(index));
|
|
if(objRef && *objRef)
|
|
return *objRef;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
int LuaInterface::getTop()
|
|
{
|
|
return lua_gettop(L);
|
|
}
|
|
|
|
void LuaInterface::loadFiles(std::string directory, bool recursive, std::string contains)
|
|
{
|
|
for(const std::string& fileName : g_resources.listDirectoryFiles(directory)) {
|
|
std::string fullPath = directory + "/" + fileName;
|
|
|
|
if(recursive && g_resources.directoryExists(fullPath)) {
|
|
loadFiles(fullPath, true, contains);
|
|
continue;
|
|
}
|
|
|
|
if(!g_resources.isFileType(fileName, "lua"))
|
|
continue;
|
|
|
|
if(!contains.empty() && fileName.find(contains) == std::string::npos)
|
|
continue;
|
|
|
|
try {
|
|
g_lua.loadScript(fullPath);
|
|
g_lua.call(0, 0);
|
|
} catch(stdext::exception& e) {
|
|
g_lua.pushString(e.what());
|
|
g_lua.error();
|
|
}
|
|
}
|
|
}
|