2013-08-04 17:11:25 -04:00
|
|
|
|
|
|
|
// LuaState.cpp
|
|
|
|
|
|
|
|
// Implements the cLuaState class representing the wrapper over lua_State *, provides associated helper functions
|
|
|
|
|
|
|
|
#include "Globals.h"
|
|
|
|
#include "LuaState.h"
|
|
|
|
|
|
|
|
extern "C"
|
|
|
|
{
|
2013-11-26 12:14:46 -05:00
|
|
|
#include "lua/src/lualib.h"
|
2013-08-04 17:11:25 -04:00
|
|
|
}
|
|
|
|
|
2014-03-25 13:15:05 -04:00
|
|
|
#undef TOLUA_TEMPLATE_BIND
|
2013-12-08 12:24:56 -05:00
|
|
|
#include "tolua++/include/tolua++.h"
|
2013-08-04 17:11:25 -04:00
|
|
|
#include "Bindings.h"
|
|
|
|
#include "ManualBindings.h"
|
2014-03-02 04:12:29 -05:00
|
|
|
#include "DeprecatedBindings.h"
|
2013-08-04 17:11:25 -04:00
|
|
|
|
|
|
|
// fwd: SQLite/lsqlite3.c
|
|
|
|
extern "C"
|
|
|
|
{
|
2013-12-27 05:51:08 -05:00
|
|
|
int luaopen_lsqlite3(lua_State * L);
|
2013-08-04 17:11:25 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// fwd: LuaExpat/lxplib.c:
|
|
|
|
extern "C"
|
|
|
|
{
|
|
|
|
int luaopen_lxp(lua_State * L);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-08-15 07:05:24 -04:00
|
|
|
const cLuaState::cRet cLuaState::Return = {};
|
2013-08-08 08:08:21 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-08-04 17:11:25 -04:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// cLuaState:
|
|
|
|
|
|
|
|
cLuaState::cLuaState(const AString & a_SubsystemName) :
|
|
|
|
m_LuaState(NULL),
|
2013-08-06 02:01:00 -04:00
|
|
|
m_IsOwned(false),
|
2013-08-06 02:59:54 -04:00
|
|
|
m_SubsystemName(a_SubsystemName),
|
|
|
|
m_NumCurrentFunctionArgs(-1)
|
2013-08-04 17:11:25 -04:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-08-06 02:01:00 -04:00
|
|
|
cLuaState::cLuaState(lua_State * a_AttachState) :
|
|
|
|
m_LuaState(a_AttachState),
|
|
|
|
m_IsOwned(false),
|
2013-08-06 02:59:54 -04:00
|
|
|
m_SubsystemName("<attached>"),
|
|
|
|
m_NumCurrentFunctionArgs(-1)
|
2013-08-06 02:01:00 -04:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-08-04 17:11:25 -04:00
|
|
|
cLuaState::~cLuaState()
|
|
|
|
{
|
|
|
|
if (IsValid())
|
|
|
|
{
|
2013-08-07 08:26:18 -04:00
|
|
|
if (m_IsOwned)
|
|
|
|
{
|
|
|
|
Close();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Detach();
|
|
|
|
}
|
2013-08-04 17:11:25 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Create(void)
|
|
|
|
{
|
|
|
|
if (m_LuaState != NULL)
|
|
|
|
{
|
|
|
|
LOGWARNING("%s: Trying to create an already-existing LuaState, ignoring.", __FUNCTION__);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
m_LuaState = lua_open();
|
|
|
|
luaL_openlibs(m_LuaState);
|
2014-03-12 08:05:28 -04:00
|
|
|
m_IsOwned = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::RegisterAPILibs(void)
|
|
|
|
{
|
2013-08-04 17:11:25 -04:00
|
|
|
tolua_AllToLua_open(m_LuaState);
|
|
|
|
ManualBindings::Bind(m_LuaState);
|
2014-03-02 04:12:29 -05:00
|
|
|
DeprecatedBindings::Bind(m_LuaState);
|
2013-08-04 17:11:25 -04:00
|
|
|
luaopen_lsqlite3(m_LuaState);
|
|
|
|
luaopen_lxp(m_LuaState);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Close(void)
|
|
|
|
{
|
|
|
|
if (m_LuaState == NULL)
|
|
|
|
{
|
|
|
|
LOGWARNING("%s: Trying to close an invalid LuaState, ignoring.", __FUNCTION__);
|
|
|
|
return;
|
|
|
|
}
|
2013-08-06 02:01:00 -04:00
|
|
|
if (!m_IsOwned)
|
|
|
|
{
|
|
|
|
LOGWARNING(
|
|
|
|
"%s: Detected mis-use, calling Close() on an attached state (0x%p). Detaching instead.",
|
|
|
|
__FUNCTION__, m_LuaState
|
|
|
|
);
|
|
|
|
Detach();
|
|
|
|
return;
|
|
|
|
}
|
2013-08-04 17:11:25 -04:00
|
|
|
lua_close(m_LuaState);
|
|
|
|
m_LuaState = NULL;
|
2013-08-06 02:01:00 -04:00
|
|
|
m_IsOwned = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Attach(lua_State * a_State)
|
|
|
|
{
|
|
|
|
if (m_LuaState != NULL)
|
|
|
|
{
|
|
|
|
LOGINFO("%s: Already contains a LuaState (0x%p), will be closed / detached.", __FUNCTION__, m_LuaState);
|
|
|
|
if (m_IsOwned)
|
|
|
|
{
|
|
|
|
Close();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Detach();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_LuaState = a_State;
|
|
|
|
m_IsOwned = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Detach(void)
|
|
|
|
{
|
|
|
|
if (m_LuaState == NULL)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (m_IsOwned)
|
|
|
|
{
|
|
|
|
LOGWARNING(
|
|
|
|
"%s: Detected a mis-use, calling Detach() when the state is owned. Closing the owned state (0x%p).",
|
|
|
|
__FUNCTION__, m_LuaState
|
|
|
|
);
|
|
|
|
Close();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
m_LuaState = NULL;
|
2013-08-04 17:11:25 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-02-17 17:12:46 -05:00
|
|
|
void cLuaState::AddPackagePath(const AString & a_PathVariable, const AString & a_Path)
|
|
|
|
{
|
|
|
|
// Get the current path:
|
|
|
|
lua_getfield(m_LuaState, LUA_GLOBALSINDEX, "package"); // Stk: <package>
|
|
|
|
lua_getfield(m_LuaState, -1, a_PathVariable.c_str()); // Stk: <package> <package.path>
|
|
|
|
size_t len = 0;
|
|
|
|
const char * PackagePath = lua_tolstring(m_LuaState, -1, &len);
|
|
|
|
|
|
|
|
// Append the new path:
|
|
|
|
AString NewPackagePath(PackagePath, len);
|
|
|
|
NewPackagePath.append(LUA_PATHSEP);
|
|
|
|
NewPackagePath.append(a_Path);
|
|
|
|
|
|
|
|
// Set the new path to the environment:
|
|
|
|
lua_pop(m_LuaState, 1); // Stk: <package>
|
|
|
|
lua_pushlstring(m_LuaState, NewPackagePath.c_str(), NewPackagePath.length()); // Stk: <package> <NewPackagePath>
|
|
|
|
lua_setfield(m_LuaState, -2, a_PathVariable.c_str()); // Stk: <package>
|
|
|
|
lua_pop(m_LuaState, 1);
|
|
|
|
lua_pop(m_LuaState, 1); // Stk: -
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-08-04 17:11:25 -04:00
|
|
|
bool cLuaState::LoadFile(const AString & a_FileName)
|
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
|
|
|
// Load the file:
|
|
|
|
int s = luaL_loadfile(m_LuaState, a_FileName.c_str());
|
|
|
|
if (ReportErrors(s))
|
|
|
|
{
|
|
|
|
LOGWARNING("Can't load %s because of an error in file %s", m_SubsystemName.c_str(), a_FileName.c_str());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Execute the globals:
|
|
|
|
s = lua_pcall(m_LuaState, 0, LUA_MULTRET, 0);
|
|
|
|
if (ReportErrors(s))
|
|
|
|
{
|
|
|
|
LOGWARNING("Error in %s in file %s", m_SubsystemName.c_str(), a_FileName.c_str());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-08-08 10:02:07 -04:00
|
|
|
bool cLuaState::HasFunction(const char * a_FunctionName)
|
|
|
|
{
|
|
|
|
if (!IsValid())
|
|
|
|
{
|
|
|
|
// This happens if cPlugin::Initialize() fails with an error
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
lua_getglobal(m_LuaState, a_FunctionName);
|
|
|
|
bool res = (!lua_isnil(m_LuaState, -1) && lua_isfunction(m_LuaState, -1));
|
|
|
|
lua_pop(m_LuaState, 1);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::PushFunction(const char * a_FunctionName)
|
2013-08-04 17:11:25 -04:00
|
|
|
{
|
2013-08-06 02:59:54 -04:00
|
|
|
ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack
|
|
|
|
|
2013-08-04 17:11:25 -04:00
|
|
|
if (!IsValid())
|
|
|
|
{
|
|
|
|
// This happens if cPlugin::Initialize() fails with an error
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-01-11 17:10:40 -05:00
|
|
|
// Push the error handler for lua_pcall()
|
|
|
|
lua_pushcfunction(m_LuaState, &ReportFnCallErrors);
|
|
|
|
|
2013-08-04 17:11:25 -04:00
|
|
|
lua_getglobal(m_LuaState, a_FunctionName);
|
|
|
|
if (!lua_isfunction(m_LuaState, -1))
|
|
|
|
{
|
2013-08-08 10:02:07 -04:00
|
|
|
LOGWARNING("Error in %s: Could not find function %s()", m_SubsystemName.c_str(), a_FunctionName);
|
2014-01-21 16:59:08 -05:00
|
|
|
lua_pop(m_LuaState, 2);
|
2013-08-04 17:11:25 -04:00
|
|
|
return false;
|
|
|
|
}
|
2013-08-06 02:59:54 -04:00
|
|
|
m_CurrentFunctionName.assign(a_FunctionName);
|
|
|
|
m_NumCurrentFunctionArgs = 0;
|
2013-08-04 17:11:25 -04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-08-08 10:02:07 -04:00
|
|
|
bool cLuaState::PushFunction(int a_FnRef)
|
2013-08-04 17:11:25 -04:00
|
|
|
{
|
2013-08-06 02:59:54 -04:00
|
|
|
ASSERT(IsValid());
|
|
|
|
ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack
|
|
|
|
|
2014-01-11 17:10:40 -05:00
|
|
|
// Push the error handler for lua_pcall()
|
|
|
|
lua_pushcfunction(m_LuaState, &ReportFnCallErrors);
|
|
|
|
|
2013-08-04 17:11:25 -04:00
|
|
|
lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, a_FnRef); // same as lua_getref()
|
|
|
|
if (!lua_isfunction(m_LuaState, -1))
|
|
|
|
{
|
2014-01-21 16:59:08 -05:00
|
|
|
lua_pop(m_LuaState, 2);
|
2013-08-04 17:11:25 -04:00
|
|
|
return false;
|
|
|
|
}
|
2013-08-06 02:59:54 -04:00
|
|
|
m_CurrentFunctionName = "<callback>";
|
|
|
|
m_NumCurrentFunctionArgs = 0;
|
2013-08-04 17:11:25 -04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-01-11 16:51:10 -05:00
|
|
|
bool cLuaState::PushFunction(const cTableRef & a_TableRef)
|
2013-08-07 08:26:18 -04:00
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack
|
2014-01-11 16:51:10 -05:00
|
|
|
|
2014-01-11 17:10:40 -05:00
|
|
|
// Push the error handler for lua_pcall()
|
|
|
|
lua_pushcfunction(m_LuaState, &ReportFnCallErrors);
|
|
|
|
|
2014-01-11 16:51:10 -05:00
|
|
|
lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, a_TableRef.GetTableRef()); // Get the table ref
|
2013-08-07 08:26:18 -04:00
|
|
|
if (!lua_istable(m_LuaState, -1))
|
|
|
|
{
|
|
|
|
// Not a table, bail out
|
2014-01-21 16:59:08 -05:00
|
|
|
lua_pop(m_LuaState, 2);
|
2013-08-07 08:26:18 -04:00
|
|
|
return false;
|
|
|
|
}
|
2014-01-11 16:51:10 -05:00
|
|
|
lua_getfield(m_LuaState, -1, a_TableRef.GetFnName());
|
2013-08-07 08:26:18 -04:00
|
|
|
if (lua_isnil(m_LuaState, -1) || !lua_isfunction(m_LuaState, -1))
|
|
|
|
{
|
|
|
|
// Not a valid function, bail out
|
2014-01-29 16:56:23 -05:00
|
|
|
lua_pop(m_LuaState, 3);
|
2013-08-07 08:26:18 -04:00
|
|
|
return false;
|
|
|
|
}
|
2014-01-29 16:56:23 -05:00
|
|
|
|
|
|
|
// Pop the table off the stack:
|
|
|
|
lua_remove(m_LuaState, -2);
|
|
|
|
|
2014-01-11 16:51:10 -05:00
|
|
|
Printf(m_CurrentFunctionName, "<table-callback %s>", a_TableRef.GetFnName());
|
2013-08-07 08:26:18 -04:00
|
|
|
m_NumCurrentFunctionArgs = 0;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-08-08 08:08:21 -04:00
|
|
|
void cLuaState::Push(const AString & a_String)
|
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
2014-01-07 09:55:23 -05:00
|
|
|
lua_pushlstring(m_LuaState, a_String.data(), a_String.size());
|
2013-08-08 08:08:21 -04:00
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(const AStringVector & a_Vector)
|
2013-08-04 17:11:25 -04:00
|
|
|
{
|
2013-08-06 02:59:54 -04:00
|
|
|
ASSERT(IsValid());
|
|
|
|
|
2013-12-27 05:51:08 -05:00
|
|
|
lua_createtable(m_LuaState, (int)a_Vector.size(), 0);
|
2013-08-04 17:11:25 -04:00
|
|
|
int newTable = lua_gettop(m_LuaState);
|
|
|
|
int index = 1;
|
|
|
|
for (AStringVector::const_iterator itr = a_Vector.begin(), end = a_Vector.end(); itr != end; ++itr, ++index)
|
|
|
|
{
|
|
|
|
tolua_pushstring(m_LuaState, itr->c_str());
|
|
|
|
lua_rawseti(m_LuaState, newTable, index);
|
|
|
|
}
|
2013-08-06 02:59:54 -04:00
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
void cLuaState::Push(const cCraftingGrid * a_Grid)
|
2013-08-06 02:59:54 -04:00
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
tolua_pushusertype(m_LuaState, (void *)a_Grid, "cCraftingGrid");
|
2013-08-06 02:59:54 -04:00
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
void cLuaState::Push(const cCraftingRecipe * a_Recipe)
|
2013-08-06 02:59:54 -04:00
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
tolua_pushusertype(m_LuaState, (void *)a_Recipe, "cCraftingRecipe");
|
2013-08-06 02:59:54 -04:00
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
void cLuaState::Push(const char * a_Value)
|
2013-08-06 02:59:54 -04:00
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
tolua_pushstring(m_LuaState, a_Value);
|
2013-08-06 02:59:54 -04:00
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
void cLuaState::Push(const cItems & a_Items)
|
2013-08-06 02:59:54 -04:00
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
tolua_pushusertype(m_LuaState, (void *)&a_Items, "cItems");
|
2013-08-06 02:59:54 -04:00
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
void cLuaState::Push(const cPlayer * a_Player)
|
2013-08-06 02:59:54 -04:00
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
tolua_pushusertype(m_LuaState, (void *)a_Player, "cPlayer");
|
2013-08-06 02:59:54 -04:00
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
void cLuaState::Push(const HTTPRequest * a_Request)
|
2013-08-06 02:59:54 -04:00
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
tolua_pushusertype(m_LuaState, (void *)a_Request, "HTTPRequest");
|
2013-08-06 02:59:54 -04:00
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
void cLuaState::Push(const HTTPTemplateRequest * a_Request)
|
2013-08-06 02:59:54 -04:00
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
tolua_pushusertype(m_LuaState, (void *)a_Request, "HTTPTemplateRequest");
|
2013-08-06 02:59:54 -04:00
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
void cLuaState::Push(const Vector3d & a_Vector)
|
2013-08-08 08:08:21 -04:00
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
tolua_pushusertype(m_LuaState, (void *)&a_Vector, "Vector3d");
|
2013-08-08 08:08:21 -04:00
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
void cLuaState::Push(bool a_Value)
|
2013-08-06 02:59:54 -04:00
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
tolua_pushboolean(m_LuaState, a_Value ? 1 : 0);
|
2013-08-06 02:59:54 -04:00
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
void cLuaState::Push(cBlockEntity * a_BlockEntity)
|
2014-03-29 10:43:03 -04:00
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
tolua_pushusertype(m_LuaState, a_BlockEntity, "cBlockEntity");
|
2014-03-29 10:43:03 -04:00
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
void cLuaState::Push(cChunkDesc * a_ChunkDesc)
|
2013-08-08 03:13:13 -04:00
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
tolua_pushusertype(m_LuaState, a_ChunkDesc, "cChunkDesc");
|
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(cClientHandle * a_Client)
|
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
|
|
|
tolua_pushusertype(m_LuaState, a_Client, "cClientHandle");
|
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(cEntity * a_Entity)
|
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
|
|
|
tolua_pushusertype(m_LuaState, a_Entity, "cEntity");
|
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(cHopperEntity * a_Hopper)
|
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
|
|
|
tolua_pushusertype(m_LuaState, a_Hopper, "cHopperEntity");
|
2013-08-08 03:13:13 -04:00
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-08-08 08:08:21 -04:00
|
|
|
void cLuaState::Push(cItem * a_Item)
|
2013-08-06 02:59:54 -04:00
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
|
|
|
tolua_pushusertype(m_LuaState, a_Item, "cItem");
|
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-08-08 08:08:21 -04:00
|
|
|
void cLuaState::Push(cItems * a_Items)
|
2013-08-06 02:59:54 -04:00
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
|
|
|
tolua_pushusertype(m_LuaState, a_Items, "cItems");
|
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
void cLuaState::Push(cMonster * a_Monster)
|
2013-12-31 08:53:10 -05:00
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
tolua_pushusertype(m_LuaState, a_Monster, "cMonster");
|
2013-12-31 08:53:10 -05:00
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
void cLuaState::Push(cPickup * a_Pickup)
|
2013-08-06 02:59:54 -04:00
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
tolua_pushusertype(m_LuaState, a_Pickup, "cPickup");
|
2013-08-06 02:59:54 -04:00
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
void cLuaState::Push(cPlayer * a_Player)
|
2013-08-06 02:59:54 -04:00
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
tolua_pushusertype(m_LuaState, a_Player, "cPlayer");
|
2013-08-06 02:59:54 -04:00
|
|
|
m_NumCurrentFunctionArgs += 1;
|
2013-08-04 17:11:25 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
void cLuaState::Push(cPluginLua * a_Plugin)
|
2013-08-08 08:08:21 -04:00
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
tolua_pushusertype(m_LuaState, a_Plugin, "cPluginLua");
|
2013-08-08 08:08:21 -04:00
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
void cLuaState::Push(cProjectileEntity * a_ProjectileEntity)
|
2013-08-08 08:08:21 -04:00
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
tolua_pushusertype(m_LuaState, a_ProjectileEntity, "cProjectileEntity");
|
2013-08-08 08:08:21 -04:00
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
void cLuaState::Push(cTNTEntity * a_TNTEntity)
|
2013-08-08 08:08:21 -04:00
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
tolua_pushusertype(m_LuaState, a_TNTEntity, "cTNTEntity");
|
2013-08-08 08:08:21 -04:00
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
void cLuaState::Push(cWebAdmin * a_WebAdmin)
|
2013-08-08 08:08:21 -04:00
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
tolua_pushusertype(m_LuaState, a_WebAdmin, "cWebAdmin");
|
2013-08-08 08:08:21 -04:00
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-08-08 10:02:07 -04:00
|
|
|
void cLuaState::Push(cWindow * a_Window)
|
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
|
|
|
tolua_pushusertype(m_LuaState, a_Window, "cWindow");
|
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
void cLuaState::Push(cWorld * a_World)
|
2013-08-08 10:02:07 -04:00
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
tolua_pushusertype(m_LuaState, a_World, "cWorld");
|
2013-08-08 10:02:07 -04:00
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
void cLuaState::Push(double a_Value)
|
2013-08-08 10:02:07 -04:00
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
tolua_pushnumber(m_LuaState, a_Value);
|
2013-08-08 10:02:07 -04:00
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
void cLuaState::Push(int a_Value)
|
2013-08-08 10:30:02 -04:00
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
tolua_pushnumber(m_LuaState, a_Value);
|
2013-08-08 10:30:02 -04:00
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
void cLuaState::Push(TakeDamageInfo * a_TDI)
|
2013-08-08 10:30:02 -04:00
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
tolua_pushusertype(m_LuaState, a_TDI, "TakeDamageInfo");
|
2013-08-08 10:30:02 -04:00
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
void cLuaState::Push(Vector3i * a_Vector)
|
Added OnExploding() and OnExploded() hooks.
As requested in FS 413, with extra parameters:
World, BlockX, BlockY, BlockZ, Size, CanCauseFire, Source, SourceData
OnExploding() can return 3 values:
StopHook, CanCauseFire, ExplosionSize
2013-08-09 08:58:43 -04:00
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
tolua_pushusertype(m_LuaState, a_Vector, "Vector3i");
|
Added OnExploding() and OnExploded() hooks.
As requested in FS 413, with extra parameters:
World, BlockX, BlockY, BlockZ, Size, CanCauseFire, Source, SourceData
OnExploding() can return 3 values:
StopHook, CanCauseFire, ExplosionSize
2013-08-09 08:58:43 -04:00
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
void cLuaState::Push(Vector3d * a_Vector)
|
Added OnExploding() and OnExploded() hooks.
As requested in FS 413, with extra parameters:
World, BlockX, BlockY, BlockZ, Size, CanCauseFire, Source, SourceData
OnExploding() can return 3 values:
StopHook, CanCauseFire, ExplosionSize
2013-08-09 08:58:43 -04:00
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
tolua_pushusertype(m_LuaState, a_Vector, "Vector3d");
|
Added OnExploding() and OnExploded() hooks.
As requested in FS 413, with extra parameters:
World, BlockX, BlockY, BlockZ, Size, CanCauseFire, Source, SourceData
OnExploding() can return 3 values:
StopHook, CanCauseFire, ExplosionSize
2013-08-09 08:58:43 -04:00
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(void * a_Ptr)
|
|
|
|
{
|
2014-03-07 13:26:07 -05:00
|
|
|
UNUSED(a_Ptr);
|
Added OnExploding() and OnExploded() hooks.
As requested in FS 413, with extra parameters:
World, BlockX, BlockY, BlockZ, Size, CanCauseFire, Source, SourceData
OnExploding() can return 3 values:
StopHook, CanCauseFire, ExplosionSize
2013-08-09 08:58:43 -04:00
|
|
|
ASSERT(IsValid());
|
|
|
|
|
2014-02-26 15:37:38 -05:00
|
|
|
// Investigate the cause of this - what is the callstack?
|
2014-03-20 04:16:47 -04:00
|
|
|
// One code path leading here is the OnHookExploding / OnHookExploded with exotic parameters. Need to decide what to do with them
|
|
|
|
LOGWARNING("Lua engine: attempting to push a plain pointer, pushing nil instead.");
|
|
|
|
LOGWARNING("This indicates an unimplemented part of MCS bindings");
|
2014-02-26 15:37:38 -05:00
|
|
|
LogStackTrace();
|
|
|
|
|
Added OnExploding() and OnExploded() hooks.
As requested in FS 413, with extra parameters:
World, BlockX, BlockY, BlockZ, Size, CanCauseFire, Source, SourceData
OnExploding() can return 3 values:
StopHook, CanCauseFire, ExplosionSize
2013-08-09 08:58:43 -04:00
|
|
|
lua_pushnil(m_LuaState);
|
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-08-11 08:57:07 -04:00
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
void cLuaState::PushUserType(void * a_Object, const char * a_Type)
|
2013-08-11 08:57:07 -04:00
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
2014-06-03 13:29:04 -04:00
|
|
|
tolua_pushusertype(m_LuaState, a_Object, a_Type);
|
2013-08-11 08:57:07 -04:00
|
|
|
m_NumCurrentFunctionArgs += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-03-04 15:55:24 -05:00
|
|
|
void cLuaState::GetStackValue(int a_StackPos, bool & a_ReturnedVal)
|
2013-08-08 08:08:21 -04:00
|
|
|
{
|
|
|
|
a_ReturnedVal = (tolua_toboolean(m_LuaState, a_StackPos, a_ReturnedVal ? 1 : 0) > 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-03-04 15:55:24 -05:00
|
|
|
void cLuaState::GetStackValue(int a_StackPos, AString & a_Value)
|
2013-08-08 08:08:21 -04:00
|
|
|
{
|
2014-03-04 15:55:24 -05:00
|
|
|
size_t len = 0;
|
|
|
|
const char * data = lua_tolstring(m_LuaState, a_StackPos, &len);
|
|
|
|
if (data != NULL)
|
|
|
|
{
|
|
|
|
a_Value.assign(data, len);
|
|
|
|
}
|
2013-08-08 08:08:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-03-04 15:55:24 -05:00
|
|
|
void cLuaState::GetStackValue(int a_StackPos, int & a_ReturnedVal)
|
2013-08-08 08:08:21 -04:00
|
|
|
{
|
|
|
|
if (lua_isnumber(m_LuaState, a_StackPos))
|
|
|
|
{
|
|
|
|
a_ReturnedVal = (int)tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal);
|
Added OnExploding() and OnExploded() hooks.
As requested in FS 413, with extra parameters:
World, BlockX, BlockY, BlockZ, Size, CanCauseFire, Source, SourceData
OnExploding() can return 3 values:
StopHook, CanCauseFire, ExplosionSize
2013-08-09 08:58:43 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-03-04 15:55:24 -05:00
|
|
|
void cLuaState::GetStackValue(int a_StackPos, double & a_ReturnedVal)
|
Added OnExploding() and OnExploded() hooks.
As requested in FS 413, with extra parameters:
World, BlockX, BlockY, BlockZ, Size, CanCauseFire, Source, SourceData
OnExploding() can return 3 values:
StopHook, CanCauseFire, ExplosionSize
2013-08-09 08:58:43 -04:00
|
|
|
{
|
|
|
|
if (lua_isnumber(m_LuaState, a_StackPos))
|
|
|
|
{
|
|
|
|
a_ReturnedVal = tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal);
|
2013-08-08 08:08:21 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-08-06 02:59:54 -04:00
|
|
|
bool cLuaState::CallFunction(int a_NumResults)
|
2013-08-04 17:11:25 -04:00
|
|
|
{
|
2013-08-06 02:59:54 -04:00
|
|
|
ASSERT (m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first
|
2014-01-11 17:10:40 -05:00
|
|
|
ASSERT(lua_isfunction(m_LuaState, -m_NumCurrentFunctionArgs - 1)); // The function to call
|
|
|
|
ASSERT(lua_isfunction(m_LuaState, -m_NumCurrentFunctionArgs - 2)); // The error handler
|
2013-08-04 17:11:25 -04:00
|
|
|
|
2014-02-11 02:52:14 -05:00
|
|
|
// Save the current "stack" state and reset, in case the callback calls another function:
|
|
|
|
AString CurrentFunctionName;
|
|
|
|
std::swap(m_CurrentFunctionName, CurrentFunctionName);
|
|
|
|
int NumArgs = m_NumCurrentFunctionArgs;
|
|
|
|
m_NumCurrentFunctionArgs = -1;
|
|
|
|
|
|
|
|
// Call the function:
|
|
|
|
int s = lua_pcall(m_LuaState, NumArgs, a_NumResults, -NumArgs - 2);
|
2014-01-11 17:10:40 -05:00
|
|
|
if (s != 0)
|
2013-08-04 17:11:25 -04:00
|
|
|
{
|
2014-01-11 17:10:40 -05:00
|
|
|
// The error has already been printed together with the stacktrace
|
2014-02-11 02:52:14 -05:00
|
|
|
LOGWARNING("Error in %s calling function %s()", m_SubsystemName.c_str(), CurrentFunctionName.c_str());
|
2013-08-04 17:11:25 -04:00
|
|
|
return false;
|
|
|
|
}
|
2014-01-21 16:59:08 -05:00
|
|
|
|
|
|
|
// Remove the error handler from the stack:
|
|
|
|
lua_remove(m_LuaState, -a_NumResults - 1);
|
|
|
|
|
2013-08-04 17:11:25 -04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-11-22 10:49:38 -05:00
|
|
|
bool cLuaState::CheckParamUserTable(int a_StartParam, const char * a_UserTable, int a_EndParam)
|
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
|
|
|
if (a_EndParam < 0)
|
|
|
|
{
|
|
|
|
a_EndParam = a_StartParam;
|
|
|
|
}
|
|
|
|
|
|
|
|
tolua_Error tolua_err;
|
|
|
|
for (int i = a_StartParam; i <= a_EndParam; i++)
|
|
|
|
{
|
|
|
|
if (tolua_isusertable(m_LuaState, i, a_UserTable, 0, &tolua_err))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// Not the correct parameter
|
|
|
|
lua_Debug entry;
|
|
|
|
VERIFY(lua_getstack(m_LuaState, 0, &entry));
|
|
|
|
VERIFY(lua_getinfo (m_LuaState, "n", &entry));
|
|
|
|
AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != NULL) ? entry.name : "?");
|
|
|
|
tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
|
|
|
|
return false;
|
|
|
|
} // for i - Param
|
|
|
|
|
|
|
|
// All params checked ok
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-08-07 08:26:18 -04:00
|
|
|
bool cLuaState::CheckParamUserType(int a_StartParam, const char * a_UserType, int a_EndParam)
|
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
|
|
|
if (a_EndParam < 0)
|
|
|
|
{
|
|
|
|
a_EndParam = a_StartParam;
|
|
|
|
}
|
|
|
|
|
|
|
|
tolua_Error tolua_err;
|
|
|
|
for (int i = a_StartParam; i <= a_EndParam; i++)
|
|
|
|
{
|
|
|
|
if (tolua_isusertype(m_LuaState, i, a_UserType, 0, &tolua_err))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// Not the correct parameter
|
|
|
|
lua_Debug entry;
|
|
|
|
VERIFY(lua_getstack(m_LuaState, 0, &entry));
|
|
|
|
VERIFY(lua_getinfo (m_LuaState, "n", &entry));
|
|
|
|
AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != NULL) ? entry.name : "?");
|
|
|
|
tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
|
|
|
|
return false;
|
|
|
|
} // for i - Param
|
|
|
|
|
|
|
|
// All params checked ok
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::CheckParamTable(int a_StartParam, int a_EndParam)
|
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
|
|
|
if (a_EndParam < 0)
|
|
|
|
{
|
|
|
|
a_EndParam = a_StartParam;
|
|
|
|
}
|
|
|
|
|
|
|
|
tolua_Error tolua_err;
|
|
|
|
for (int i = a_StartParam; i <= a_EndParam; i++)
|
|
|
|
{
|
|
|
|
if (tolua_istable(m_LuaState, i, 0, &tolua_err))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// Not the correct parameter
|
|
|
|
lua_Debug entry;
|
|
|
|
VERIFY(lua_getstack(m_LuaState, 0, &entry));
|
|
|
|
VERIFY(lua_getinfo (m_LuaState, "n", &entry));
|
|
|
|
AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != NULL) ? entry.name : "?");
|
|
|
|
tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
|
|
|
|
return false;
|
|
|
|
} // for i - Param
|
|
|
|
|
|
|
|
// All params checked ok
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::CheckParamNumber(int a_StartParam, int a_EndParam)
|
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
|
|
|
if (a_EndParam < 0)
|
|
|
|
{
|
|
|
|
a_EndParam = a_StartParam;
|
|
|
|
}
|
|
|
|
|
|
|
|
tolua_Error tolua_err;
|
|
|
|
for (int i = a_StartParam; i <= a_EndParam; i++)
|
|
|
|
{
|
|
|
|
if (tolua_isnumber(m_LuaState, i, 0, &tolua_err))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// Not the correct parameter
|
|
|
|
lua_Debug entry;
|
|
|
|
VERIFY(lua_getstack(m_LuaState, 0, &entry));
|
|
|
|
VERIFY(lua_getinfo (m_LuaState, "n", &entry));
|
|
|
|
AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != NULL) ? entry.name : "?");
|
|
|
|
tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
|
|
|
|
return false;
|
|
|
|
} // for i - Param
|
|
|
|
|
|
|
|
// All params checked ok
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-11-22 14:11:24 -05:00
|
|
|
bool cLuaState::CheckParamString(int a_StartParam, int a_EndParam)
|
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
|
|
|
if (a_EndParam < 0)
|
|
|
|
{
|
|
|
|
a_EndParam = a_StartParam;
|
|
|
|
}
|
|
|
|
|
|
|
|
tolua_Error tolua_err;
|
|
|
|
for (int i = a_StartParam; i <= a_EndParam; i++)
|
|
|
|
{
|
|
|
|
if (tolua_isstring(m_LuaState, i, 0, &tolua_err))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// Not the correct parameter
|
|
|
|
lua_Debug entry;
|
|
|
|
VERIFY(lua_getstack(m_LuaState, 0, &entry));
|
|
|
|
VERIFY(lua_getinfo (m_LuaState, "n", &entry));
|
|
|
|
AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != NULL) ? entry.name : "?");
|
|
|
|
tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
|
|
|
|
return false;
|
|
|
|
} // for i - Param
|
|
|
|
|
|
|
|
// All params checked ok
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-01-19 17:45:26 -05:00
|
|
|
bool cLuaState::CheckParamFunction(int a_StartParam, int a_EndParam)
|
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
|
|
|
if (a_EndParam < 0)
|
|
|
|
{
|
|
|
|
a_EndParam = a_StartParam;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = a_StartParam; i <= a_EndParam; i++)
|
|
|
|
{
|
|
|
|
if (lua_isfunction(m_LuaState, i))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// Not the correct parameter
|
|
|
|
lua_Debug entry;
|
|
|
|
VERIFY(lua_getstack(m_LuaState, 0, &entry));
|
|
|
|
VERIFY(lua_getinfo (m_LuaState, "n", &entry));
|
2014-02-11 09:03:35 -05:00
|
|
|
luaL_error(m_LuaState, "Error in function '%s' parameter #%d. Function expected, got %s",
|
|
|
|
(entry.name != NULL) ? entry.name : "?", i, GetTypeText(i).c_str()
|
|
|
|
);
|
|
|
|
return false;
|
|
|
|
} // for i - Param
|
|
|
|
|
|
|
|
// All params checked ok
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::CheckParamFunctionOrNil(int a_StartParam, int a_EndParam)
|
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
|
|
|
|
if (a_EndParam < 0)
|
|
|
|
{
|
|
|
|
a_EndParam = a_StartParam;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = a_StartParam; i <= a_EndParam; i++)
|
|
|
|
{
|
|
|
|
if (lua_isfunction(m_LuaState, i) || lua_isnil(m_LuaState, i))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// Not the correct parameter
|
|
|
|
lua_Debug entry;
|
|
|
|
VERIFY(lua_getstack(m_LuaState, 0, &entry));
|
|
|
|
VERIFY(lua_getinfo (m_LuaState, "n", &entry));
|
|
|
|
luaL_error(m_LuaState, "Error in function '%s' parameter #%d. Function expected, got %s",
|
2014-01-19 17:45:26 -05:00
|
|
|
(entry.name != NULL) ? entry.name : "?", i, GetTypeText(i).c_str()
|
|
|
|
);
|
|
|
|
return false;
|
|
|
|
} // for i - Param
|
|
|
|
|
|
|
|
// All params checked ok
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-08-07 08:26:18 -04:00
|
|
|
bool cLuaState::CheckParamEnd(int a_Param)
|
|
|
|
{
|
|
|
|
tolua_Error tolua_err;
|
|
|
|
if (tolua_isnoobj(m_LuaState, a_Param, &tolua_err))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// Not the correct parameter
|
|
|
|
lua_Debug entry;
|
|
|
|
VERIFY(lua_getstack(m_LuaState, 0, &entry));
|
|
|
|
VERIFY(lua_getinfo (m_LuaState, "n", &entry));
|
|
|
|
AString ErrMsg = Printf("#ferror in function '%s': Too many arguments.", (entry.name != NULL) ? entry.name : "?");
|
|
|
|
tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-08-04 17:11:25 -04:00
|
|
|
bool cLuaState::ReportErrors(int a_Status)
|
|
|
|
{
|
|
|
|
return ReportErrors(m_LuaState, a_Status);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::ReportErrors(lua_State * a_LuaState, int a_Status)
|
|
|
|
{
|
|
|
|
if (a_Status == 0)
|
|
|
|
{
|
|
|
|
// No error to report
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOGWARNING("LUA: %d - %s", a_Status, lua_tostring(a_LuaState, -1));
|
|
|
|
lua_pop(a_LuaState, 1);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-08-07 08:26:18 -04:00
|
|
|
|
2014-03-19 17:55:47 -04:00
|
|
|
void cLuaState::LogStackTrace(int a_StartingDepth)
|
2014-01-11 16:51:10 -05:00
|
|
|
{
|
2014-03-19 17:55:47 -04:00
|
|
|
LogStackTrace(m_LuaState, a_StartingDepth);
|
2014-01-11 16:51:10 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-03-19 17:55:47 -04:00
|
|
|
void cLuaState::LogStackTrace(lua_State * a_LuaState, int a_StartingDepth)
|
2013-08-21 14:06:37 -04:00
|
|
|
{
|
|
|
|
LOGWARNING("Stack trace:");
|
|
|
|
lua_Debug entry;
|
2014-03-19 17:55:47 -04:00
|
|
|
int depth = a_StartingDepth;
|
2014-01-11 16:51:10 -05:00
|
|
|
while (lua_getstack(a_LuaState, depth, &entry))
|
2013-08-21 14:06:37 -04:00
|
|
|
{
|
2014-02-04 16:24:03 -05:00
|
|
|
lua_getinfo(a_LuaState, "Sln", &entry);
|
2014-01-11 16:51:10 -05:00
|
|
|
LOGWARNING(" %s(%d): %s", entry.short_src, entry.currentline, entry.name ? entry.name : "(no name)");
|
2013-08-21 14:06:37 -04:00
|
|
|
depth++;
|
|
|
|
}
|
|
|
|
LOGWARNING("Stack trace end");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AString cLuaState::GetTypeText(int a_StackPos)
|
|
|
|
{
|
2014-01-21 16:59:08 -05:00
|
|
|
return lua_typename(m_LuaState, lua_type(m_LuaState, a_StackPos));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int cLuaState::CallFunctionWithForeignParams(
|
|
|
|
const AString & a_FunctionName,
|
|
|
|
cLuaState & a_SrcLuaState,
|
|
|
|
int a_SrcParamStart,
|
|
|
|
int a_SrcParamEnd
|
|
|
|
)
|
|
|
|
{
|
|
|
|
ASSERT(IsValid());
|
|
|
|
ASSERT(a_SrcLuaState.IsValid());
|
|
|
|
|
|
|
|
// Store the stack position before any changes
|
|
|
|
int OldTop = lua_gettop(m_LuaState);
|
|
|
|
|
|
|
|
// Push the function to call, including the error handler:
|
|
|
|
if (!PushFunction(a_FunctionName.c_str()))
|
|
|
|
{
|
|
|
|
LOGWARNING("Function '%s' not found", a_FunctionName.c_str());
|
|
|
|
lua_pop(m_LuaState, 2);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy the function parameters to the target state
|
|
|
|
if (CopyStackFrom(a_SrcLuaState, a_SrcParamStart, a_SrcParamEnd) < 0)
|
2013-08-21 14:06:37 -04:00
|
|
|
{
|
2014-01-21 16:59:08 -05:00
|
|
|
// Something went wrong, fix the stack and exit
|
|
|
|
lua_pop(m_LuaState, 2);
|
2014-02-04 03:18:32 -05:00
|
|
|
m_NumCurrentFunctionArgs = -1;
|
|
|
|
m_CurrentFunctionName.clear();
|
2014-01-21 16:59:08 -05:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Call the function, with an error handler:
|
2014-02-04 04:29:10 -05:00
|
|
|
int s = lua_pcall(m_LuaState, a_SrcParamEnd - a_SrcParamStart + 1, LUA_MULTRET, OldTop + 1);
|
2014-01-21 16:59:08 -05:00
|
|
|
if (ReportErrors(s))
|
|
|
|
{
|
|
|
|
LOGWARN("Error while calling function '%s' in '%s'", a_FunctionName.c_str(), m_SubsystemName.c_str());
|
|
|
|
// Fix the stack.
|
|
|
|
// We don't know how many values have been pushed, so just get rid of any that weren't there initially
|
|
|
|
int CurTop = lua_gettop(m_LuaState);
|
|
|
|
if (CurTop > OldTop)
|
|
|
|
{
|
|
|
|
lua_pop(m_LuaState, CurTop - OldTop);
|
|
|
|
}
|
2014-02-04 03:18:32 -05:00
|
|
|
|
|
|
|
// Reset the internal checking mechanisms:
|
|
|
|
m_NumCurrentFunctionArgs = -1;
|
|
|
|
m_CurrentFunctionName.clear();
|
|
|
|
|
2014-02-04 04:29:10 -05:00
|
|
|
// Make Lua think everything is okay and return 0 values, so that plugins continue executing.
|
|
|
|
// The failure is indicated by the zero return values.
|
|
|
|
return 0;
|
2013-08-21 14:06:37 -04:00
|
|
|
}
|
2014-01-21 16:59:08 -05:00
|
|
|
|
|
|
|
// Reset the internal checking mechanisms:
|
|
|
|
m_NumCurrentFunctionArgs = -1;
|
2014-02-04 03:18:32 -05:00
|
|
|
m_CurrentFunctionName.clear();
|
2014-01-21 16:59:08 -05:00
|
|
|
|
|
|
|
// Remove the error handler from the stack:
|
|
|
|
lua_remove(m_LuaState, OldTop + 1);
|
|
|
|
|
|
|
|
// Return the number of return values:
|
|
|
|
return lua_gettop(m_LuaState) - OldTop;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int cLuaState::CopyStackFrom(cLuaState & a_SrcLuaState, int a_SrcStart, int a_SrcEnd)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
// DEBUG:
|
|
|
|
LOGD("Copying stack values from %d to %d", a_SrcStart, a_SrcEnd);
|
|
|
|
a_SrcLuaState.LogStack("Src stack before copying:");
|
|
|
|
LogStack("Dst stack before copying:");
|
|
|
|
*/
|
|
|
|
for (int i = a_SrcStart; i <= a_SrcEnd; ++i)
|
|
|
|
{
|
|
|
|
int t = lua_type(a_SrcLuaState, i);
|
|
|
|
switch (t)
|
|
|
|
{
|
|
|
|
case LUA_TNIL:
|
|
|
|
{
|
|
|
|
lua_pushnil(m_LuaState);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case LUA_TSTRING:
|
|
|
|
{
|
|
|
|
AString s;
|
|
|
|
a_SrcLuaState.ToString(i, s);
|
|
|
|
Push(s);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case LUA_TBOOLEAN:
|
|
|
|
{
|
|
|
|
bool b = (tolua_toboolean(a_SrcLuaState, i, false) != 0);
|
|
|
|
Push(b);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case LUA_TNUMBER:
|
|
|
|
{
|
|
|
|
lua_Number d = tolua_tonumber(a_SrcLuaState, i, 0);
|
|
|
|
Push(d);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case LUA_TUSERDATA:
|
|
|
|
{
|
|
|
|
// Get the class name:
|
|
|
|
const char * type = NULL;
|
|
|
|
if (lua_getmetatable(a_SrcLuaState, i) == 0)
|
|
|
|
{
|
|
|
|
LOGWARNING("%s: Unknown class in pos %d, cannot copy.", __FUNCTION__, i);
|
|
|
|
lua_pop(m_LuaState, i - a_SrcStart);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
lua_rawget(a_SrcLuaState, LUA_REGISTRYINDEX); // Stack +1
|
|
|
|
type = lua_tostring(a_SrcLuaState, -1);
|
|
|
|
lua_pop(a_SrcLuaState, 1); // Stack -1
|
|
|
|
|
|
|
|
// Copy the value:
|
|
|
|
void * ud = tolua_touserdata(a_SrcLuaState, i, NULL);
|
|
|
|
tolua_pushusertype(m_LuaState, ud, type);
|
2014-02-04 08:26:36 -05:00
|
|
|
break;
|
2014-01-21 16:59:08 -05:00
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
LOGWARNING("%s: Unsupported value: '%s' at stack position %d. Can only copy numbers, strings, bools and classes!",
|
|
|
|
__FUNCTION__, lua_typename(a_SrcLuaState, t), i
|
|
|
|
);
|
|
|
|
a_SrcLuaState.LogStack("Stack where copying failed:");
|
|
|
|
lua_pop(m_LuaState, i - a_SrcStart);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return a_SrcEnd - a_SrcStart + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::ToString(int a_StackPos, AString & a_String)
|
|
|
|
{
|
|
|
|
size_t len;
|
|
|
|
const char * s = lua_tolstring(m_LuaState, a_StackPos, &len);
|
|
|
|
if (s != NULL)
|
|
|
|
{
|
|
|
|
a_String.assign(s, len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::LogStack(const char * a_Header)
|
|
|
|
{
|
|
|
|
LogStack(m_LuaState, a_Header);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::LogStack(lua_State * a_LuaState, const char * a_Header)
|
|
|
|
{
|
2014-02-26 15:37:38 -05:00
|
|
|
UNUSED(a_Header); // The param seems unused when compiling for release, so the compiler warns
|
|
|
|
|
2014-03-11 17:16:08 -04:00
|
|
|
|
|
|
|
// Format string consisting only of %s is used to appease the compiler
|
|
|
|
LOGD("%s",(a_Header != NULL) ? a_Header : "Lua C API Stack contents:");
|
2014-02-10 16:44:56 -05:00
|
|
|
for (int i = lua_gettop(a_LuaState); i > 0; i--)
|
2014-01-21 16:59:08 -05:00
|
|
|
{
|
|
|
|
AString Value;
|
|
|
|
int Type = lua_type(a_LuaState, i);
|
|
|
|
switch (Type)
|
|
|
|
{
|
|
|
|
case LUA_TBOOLEAN: Value.assign((lua_toboolean(a_LuaState, i) != 0) ? "true" : "false"); break;
|
|
|
|
case LUA_TLIGHTUSERDATA: Printf(Value, "%p", lua_touserdata(a_LuaState, i)); break;
|
|
|
|
case LUA_TNUMBER: Printf(Value, "%f", (double)lua_tonumber(a_LuaState, i)); break;
|
|
|
|
case LUA_TSTRING: Printf(Value, "%s", lua_tostring(a_LuaState, i)); break;
|
2014-02-17 17:12:46 -05:00
|
|
|
case LUA_TTABLE: Printf(Value, "%p", lua_topointer(a_LuaState, i)); break;
|
2014-01-21 16:59:08 -05:00
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
LOGD(" Idx %d: type %d (%s) %s", i, Type, lua_typename(a_LuaState, Type), Value.c_str());
|
|
|
|
} // for i - stack idx
|
2013-08-21 14:06:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-01-11 17:10:40 -05:00
|
|
|
int cLuaState::ReportFnCallErrors(lua_State * a_LuaState)
|
|
|
|
{
|
|
|
|
LOGWARNING("LUA: %s", lua_tostring(a_LuaState, -1));
|
2014-03-19 17:55:47 -04:00
|
|
|
LogStackTrace(a_LuaState, 1);
|
2014-01-11 17:10:40 -05:00
|
|
|
return 1; // We left the error message on the stack as the return value
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-08-07 08:26:18 -04:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// cLuaState::cRef:
|
|
|
|
|
2014-02-09 12:39:22 -05:00
|
|
|
cLuaState::cRef::cRef(void) :
|
|
|
|
m_LuaState(NULL),
|
|
|
|
m_Ref(LUA_REFNIL)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-08-07 08:26:18 -04:00
|
|
|
cLuaState::cRef::cRef(cLuaState & a_LuaState, int a_StackPos) :
|
2014-02-09 12:39:22 -05:00
|
|
|
m_LuaState(NULL),
|
|
|
|
m_Ref(LUA_REFNIL)
|
2013-08-07 08:26:18 -04:00
|
|
|
{
|
2014-02-09 12:39:22 -05:00
|
|
|
RefStack(a_LuaState, a_StackPos);
|
2013-08-07 08:26:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cLuaState::cRef::~cRef()
|
|
|
|
{
|
2014-02-09 12:39:22 -05:00
|
|
|
if (m_LuaState != NULL)
|
|
|
|
{
|
|
|
|
UnRef();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::cRef::RefStack(cLuaState & a_LuaState, int a_StackPos)
|
|
|
|
{
|
|
|
|
ASSERT(a_LuaState.IsValid());
|
|
|
|
if (m_LuaState != NULL)
|
|
|
|
{
|
|
|
|
UnRef();
|
|
|
|
}
|
|
|
|
m_LuaState = &a_LuaState;
|
|
|
|
lua_pushvalue(a_LuaState, a_StackPos); // Push a copy of the value at a_StackPos onto the stack
|
|
|
|
m_Ref = luaL_ref(a_LuaState, LUA_REGISTRYINDEX);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::cRef::UnRef(void)
|
|
|
|
{
|
|
|
|
ASSERT(m_LuaState->IsValid()); // The reference should be destroyed before destroying the LuaState
|
2013-08-07 08:26:18 -04:00
|
|
|
|
|
|
|
if (IsValid())
|
|
|
|
{
|
2014-02-09 12:39:22 -05:00
|
|
|
luaL_unref(*m_LuaState, LUA_REGISTRYINDEX, m_Ref);
|
2013-08-07 08:26:18 -04:00
|
|
|
}
|
2014-02-09 12:39:22 -05:00
|
|
|
m_LuaState = NULL;
|
|
|
|
m_Ref = LUA_REFNIL;
|
2013-08-07 08:26:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|