One thread may untrack a ref while another thread was closing the LuaState, which had emptied the tracked refs.
2387 lines
48 KiB
C++
2387 lines
48 KiB
C++
|
|
// LuaState.cpp
|
|
|
|
// Implements the cLuaState class representing the wrapper over lua_State *, provides associated helper functions
|
|
|
|
#include "Globals.h"
|
|
#include "LuaState.h"
|
|
|
|
extern "C"
|
|
{
|
|
#include "lua/src/lualib.h"
|
|
}
|
|
|
|
#undef TOLUA_TEMPLATE_BIND
|
|
#include "tolua++/include/tolua++.h"
|
|
#include "Bindings.h"
|
|
#include "ManualBindings.h"
|
|
#include "DeprecatedBindings.h"
|
|
#include "LuaJson.h"
|
|
#include "../Entities/Entity.h"
|
|
#include "../BlockEntities/BlockEntity.h"
|
|
|
|
|
|
|
|
|
|
|
|
// fwd: "SQLite/lsqlite3.c"
|
|
extern "C"
|
|
{
|
|
int luaopen_lsqlite3(lua_State * L);
|
|
}
|
|
|
|
// fwd: "LuaExpat/lxplib.c":
|
|
extern "C"
|
|
{
|
|
int luaopen_lxp(lua_State * L);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const cLuaState::cRet cLuaState::Return = {};
|
|
const cLuaState::cNil cLuaState::Nil = {};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// cCanonLuaStates:
|
|
|
|
/** Tracks the canon cLuaState instances for each lua_State pointer.
|
|
Used for tracked refs - the ref needs to be tracked by a single cLuaState (the canon state), despite being created from a different (attached) cLuaState.
|
|
The canon state must be available without accessing the Lua state itself (so it cannot be stored within Lua). */
|
|
class cCanonLuaStates
|
|
{
|
|
public:
|
|
/** Returns the canon Lua state for the specified lua_State pointer. */
|
|
static cLuaState * GetCanonState(lua_State * a_LuaState)
|
|
{
|
|
auto & inst = GetInstance();
|
|
cCSLock lock(inst.m_CS);
|
|
auto itr = inst.m_CanonStates.find(a_LuaState);
|
|
if (itr == inst.m_CanonStates.end())
|
|
{
|
|
return nullptr;
|
|
}
|
|
return itr->second;
|
|
}
|
|
|
|
/** Adds a new canon cLuaState instance to the map.
|
|
Used when a new Lua state is created, this informs the map that a new canon Lua state should be tracked. */
|
|
static void Add(cLuaState & a_LuaState)
|
|
{
|
|
auto & inst = GetInstance();
|
|
cCSLock lock(inst.m_CS);
|
|
ASSERT(inst.m_CanonStates.find(a_LuaState) == inst.m_CanonStates.end());
|
|
inst.m_CanonStates[a_LuaState.operator lua_State *()] = &a_LuaState;
|
|
}
|
|
|
|
/** Removes the bindings between the specified canon state and its lua_State pointer.
|
|
Used when a Lua state is being closed. */
|
|
static void Remove(cLuaState & a_LuaState)
|
|
{
|
|
auto & inst = GetInstance();
|
|
cCSLock lock(inst.m_CS);
|
|
auto itr = inst.m_CanonStates.find(a_LuaState);
|
|
ASSERT(itr != inst.m_CanonStates.end());
|
|
inst.m_CanonStates.erase(itr);
|
|
}
|
|
|
|
protected:
|
|
/** The mutex protecting m_CanonStates against multithreaded access. */
|
|
cCriticalSection m_CS;
|
|
|
|
/** Map of lua_State pointers to their canon cLuaState instances. */
|
|
std::map<lua_State *, cLuaState *> m_CanonStates;
|
|
|
|
|
|
/** Returns the singleton instance of this class. */
|
|
static cCanonLuaStates & GetInstance(void)
|
|
{
|
|
static cCanonLuaStates canonLuaStates;
|
|
return canonLuaStates;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// cLuaStateTracker:
|
|
|
|
void cLuaStateTracker::Add(cLuaState & a_LuaState)
|
|
{
|
|
auto & Instance = Get();
|
|
cCSLock Lock(Instance.m_CSLuaStates);
|
|
Instance.m_LuaStates.push_back(&a_LuaState);
|
|
}
|
|
|
|
|
|
|
|
|
|
void cLuaStateTracker::Del(cLuaState & a_LuaState)
|
|
{
|
|
auto & Instance = Get();
|
|
cCSLock Lock(Instance.m_CSLuaStates);
|
|
Instance.m_LuaStates.erase(
|
|
std::remove_if(
|
|
Instance.m_LuaStates.begin(), Instance.m_LuaStates.end(),
|
|
[&a_LuaState](cLuaStatePtr a_StoredLuaState)
|
|
{
|
|
return (&a_LuaState == a_StoredLuaState);
|
|
}
|
|
),
|
|
Instance.m_LuaStates.end()
|
|
);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
AString cLuaStateTracker::GetStats(void)
|
|
{
|
|
auto & Instance = Get();
|
|
cCSLock Lock(Instance.m_CSLuaStates);
|
|
AString res;
|
|
int Total = 0;
|
|
for (auto state: Instance.m_LuaStates)
|
|
{
|
|
int Mem = 0;
|
|
if (!state->Call("collectgarbage", "count", cLuaState::Return, Mem))
|
|
{
|
|
res.append(Printf("Cannot query memory for state \"%s\"\n", state->GetSubsystemName().c_str()));
|
|
}
|
|
else
|
|
{
|
|
res.append(Printf("State \"%s\" is using %d KiB of memory\n", state->GetSubsystemName().c_str(), Mem));
|
|
Total += Mem;
|
|
}
|
|
}
|
|
res.append(Printf("Total memory used by Lua: %d KiB\n", Total));
|
|
return res;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cLuaStateTracker & cLuaStateTracker::Get(void)
|
|
{
|
|
static cLuaStateTracker Inst; // The singleton
|
|
return Inst;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// cLuaState::cTrackedRef:
|
|
|
|
cLuaState::cTrackedRef::cTrackedRef(void):
|
|
m_CS(nullptr)
|
|
{
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::cTrackedRef::RefStack(cLuaState & a_LuaState, int a_StackPos)
|
|
{
|
|
// Clear any previous callback:
|
|
Clear();
|
|
|
|
// Add self to LuaState's callback-tracking:
|
|
auto canonState = a_LuaState.QueryCanonLuaState();
|
|
canonState->TrackRef(*this);
|
|
|
|
// Store the new callback:
|
|
m_CS = &(canonState->m_CS);
|
|
m_Ref.RefStack(*canonState, a_StackPos);
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::cTrackedRef::Clear(void)
|
|
{
|
|
// Free the reference:
|
|
lua_State * luaState = nullptr;
|
|
{
|
|
auto cs = m_CS.load();
|
|
if (cs != nullptr)
|
|
{
|
|
cCSLock Lock(*cs);
|
|
if (!m_Ref.IsValid())
|
|
{
|
|
return;
|
|
}
|
|
luaState = m_Ref.GetLuaState();
|
|
m_Ref.UnRef();
|
|
}
|
|
}
|
|
m_CS = nullptr;
|
|
|
|
// Remove from LuaState's callback-tracking:
|
|
if (luaState == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
cLuaState(luaState).UntrackRef(*this);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::cTrackedRef::IsValid(void)
|
|
{
|
|
auto cs = m_CS.load();
|
|
if (cs == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
cCSLock lock(*cs);
|
|
return m_Ref.IsValid();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::cTrackedRef::IsSameLuaState(cLuaState & a_LuaState)
|
|
{
|
|
auto cs = m_CS.load();
|
|
if (cs == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
cCSLock lock(*cs);
|
|
if (!m_Ref.IsValid())
|
|
{
|
|
return false;
|
|
}
|
|
auto canonState = a_LuaState.QueryCanonLuaState();
|
|
if (canonState == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
return (m_Ref.GetLuaState() == static_cast<lua_State *>(*canonState));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::cTrackedRef::Invalidate(void)
|
|
{
|
|
auto cs = m_CS.load();
|
|
if (cs == nullptr)
|
|
{
|
|
// Already invalid
|
|
return;
|
|
}
|
|
cCSLock Lock(*cs);
|
|
if (!m_Ref.IsValid())
|
|
{
|
|
LOGD("%s: Inconsistent callback at %p, has a CS but an invalid Ref. This should not happen",
|
|
__FUNCTION__, reinterpret_cast<void *>(this)
|
|
);
|
|
return;
|
|
}
|
|
m_Ref.UnRef();
|
|
m_CS = nullptr;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// cLuaState::cCallback:
|
|
|
|
bool cLuaState::cCallback::RefStack(cLuaState & a_LuaState, int a_StackPos)
|
|
{
|
|
// Check if the stack contains a function:
|
|
if (!lua_isfunction(a_LuaState, a_StackPos))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return Super::RefStack(a_LuaState, a_StackPos);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// cLuaState::cOptionalCallback:
|
|
|
|
bool cLuaState::cOptionalCallback::RefStack(cLuaState & a_LuaState, int a_StackPos)
|
|
{
|
|
// If the stack pos is nil, make this an empty callback:
|
|
if (lua_isnil(a_LuaState, a_StackPos))
|
|
{
|
|
Clear();
|
|
return true;
|
|
}
|
|
|
|
// Use default cCallback implementation:
|
|
return Super::RefStack(a_LuaState, a_StackPos);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// cLuaState::cTableRef:
|
|
|
|
bool cLuaState::cTableRef::RefStack(cLuaState & a_LuaState, int a_StackPos)
|
|
{
|
|
// Check if the stack contains a table:
|
|
if (!lua_istable(a_LuaState, a_StackPos))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return Super::RefStack(a_LuaState, a_StackPos);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// cLuaState::cStackTable:
|
|
|
|
cLuaState::cStackTable::cStackTable(cLuaState & a_LuaState, int a_StackPos):
|
|
m_LuaState(a_LuaState),
|
|
m_StackPos(a_StackPos)
|
|
{
|
|
ASSERT(lua_istable(a_LuaState, a_StackPos));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::cStackTable::ForEachArrayElement(std::function<bool(cLuaState & a_LuaState, int a_Index)> a_ElementCallback) const
|
|
{
|
|
auto numElements = luaL_getn(m_LuaState, m_StackPos);
|
|
#ifdef _DEBUG
|
|
auto stackTop = lua_gettop(m_LuaState);
|
|
#endif
|
|
for (int idx = 1; idx <= numElements; idx++)
|
|
{
|
|
// Push the idx-th element of the array onto stack top and call the callback:
|
|
lua_rawgeti(m_LuaState, m_StackPos, idx);
|
|
auto shouldAbort = a_ElementCallback(m_LuaState, idx);
|
|
ASSERT(lua_gettop(m_LuaState) == stackTop + 1); // The element callback must not change the Lua stack below the value
|
|
lua_pop(m_LuaState, 1);
|
|
if (shouldAbort)
|
|
{
|
|
// The callback wants to abort
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::cStackTable::ForEachElement(std::function<bool(cLuaState & a_LuaState)> a_ElementCallback) const
|
|
{
|
|
#ifdef _DEBUG
|
|
auto stackTop = lua_gettop(m_LuaState);
|
|
#endif
|
|
lua_pushvalue(m_LuaState, m_StackPos); // Stk: <table>
|
|
lua_pushnil(m_LuaState); // Stk: <table> nil
|
|
while (lua_next(m_LuaState, -2)) // Stk: <table> <key> <val>
|
|
{
|
|
auto shouldAbort = a_ElementCallback(m_LuaState);
|
|
ASSERT(lua_gettop(m_LuaState) == stackTop + 3); // The element callback must not change the Lua stack below the value
|
|
lua_pop(m_LuaState, 1); // Stk: <table> <key>
|
|
if (shouldAbort)
|
|
{
|
|
// The callback wants to abort
|
|
lua_pop(m_LuaState, 2); // Stk: empty
|
|
return;
|
|
}
|
|
}
|
|
// Stk: <table>
|
|
lua_pop(m_LuaState, 1); // Stk: empty
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// cLuaState:
|
|
|
|
cLuaState::cLuaState(const AString & a_SubsystemName) :
|
|
m_LuaState(nullptr),
|
|
m_IsOwned(false),
|
|
m_SubsystemName(a_SubsystemName),
|
|
m_NumCurrentFunctionArgs(-1)
|
|
{
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cLuaState::cLuaState(lua_State * a_AttachState) :
|
|
m_LuaState(a_AttachState),
|
|
m_IsOwned(false),
|
|
m_SubsystemName("<attached>"),
|
|
m_NumCurrentFunctionArgs(-1)
|
|
{
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cLuaState::~cLuaState()
|
|
{
|
|
if (IsValid())
|
|
{
|
|
if (m_IsOwned)
|
|
{
|
|
Close();
|
|
}
|
|
else
|
|
{
|
|
Detach();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Create(void)
|
|
{
|
|
if (m_LuaState != nullptr)
|
|
{
|
|
LOGWARNING("%s: Trying to create an already-existing LuaState, ignoring.", __FUNCTION__);
|
|
return;
|
|
}
|
|
m_LuaState = lua_open();
|
|
luaL_openlibs(m_LuaState);
|
|
m_IsOwned = true;
|
|
cLuaStateTracker::Add(*this);
|
|
cCanonLuaStates::Add(*this);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::RegisterAPILibs(void)
|
|
{
|
|
auto top = lua_gettop(m_LuaState);
|
|
tolua_AllToLua_open(m_LuaState);
|
|
ASSERT(top == lua_gettop(m_LuaState));
|
|
cManualBindings::Bind(m_LuaState);
|
|
ASSERT(top == lua_gettop(m_LuaState));
|
|
DeprecatedBindings::Bind(m_LuaState);
|
|
ASSERT(top == lua_gettop(m_LuaState));
|
|
cLuaJson::Bind(*this);
|
|
ASSERT(top == lua_gettop(m_LuaState));
|
|
luaopen_lsqlite3(m_LuaState);
|
|
if (top == lua_gettop(m_LuaState) - 1)
|
|
{
|
|
// lsqlite3 left the "sqlite3" table on the stack, pop it:
|
|
lua_pop(m_LuaState, 1);
|
|
}
|
|
ASSERT(top == lua_gettop(m_LuaState));
|
|
luaopen_lxp(m_LuaState);
|
|
if (top == lua_gettop(m_LuaState) - 1)
|
|
{
|
|
// lxp left the unregistered "lxp" table on the stack, register and pop it (#3304):
|
|
lua_setglobal(m_LuaState, "lxp");
|
|
}
|
|
ASSERT(top == lua_gettop(m_LuaState));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Close(void)
|
|
{
|
|
if (m_LuaState == nullptr)
|
|
{
|
|
LOGWARNING("%s: Trying to close an invalid LuaState, ignoring.", __FUNCTION__);
|
|
return;
|
|
}
|
|
if (!m_IsOwned)
|
|
{
|
|
LOGWARNING(
|
|
"%s: Detected mis-use, calling Close() on an attached state (0x%p). Detaching instead.",
|
|
__FUNCTION__, static_cast<void *>(m_LuaState)
|
|
);
|
|
Detach();
|
|
return;
|
|
}
|
|
|
|
// Invalidate all tracked refs:
|
|
{
|
|
cCSLock Lock(m_CSTrackedRefs);
|
|
for (auto & r: m_TrackedRefs)
|
|
{
|
|
r->Invalidate();
|
|
}
|
|
m_TrackedRefs.clear();
|
|
}
|
|
|
|
cCanonLuaStates::Remove(*this);
|
|
cLuaStateTracker::Del(*this);
|
|
lua_close(m_LuaState);
|
|
m_LuaState = nullptr;
|
|
m_IsOwned = false;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Attach(lua_State * a_State)
|
|
{
|
|
if (m_LuaState != nullptr)
|
|
{
|
|
LOGINFO("%s: Already contains a LuaState (0x%p), will be closed / detached.", __FUNCTION__, static_cast<void *>(m_LuaState));
|
|
if (m_IsOwned)
|
|
{
|
|
Close();
|
|
}
|
|
else
|
|
{
|
|
Detach();
|
|
}
|
|
}
|
|
m_LuaState = a_State;
|
|
m_IsOwned = false;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Detach(void)
|
|
{
|
|
if (m_LuaState == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
if (m_IsOwned)
|
|
{
|
|
LOGWARNING(
|
|
"%s: Detected a mis-use, calling Detach() when the state is owned. Closing the owned state (0x%p).",
|
|
__FUNCTION__, static_cast<void *>(m_LuaState)
|
|
);
|
|
Close();
|
|
return;
|
|
}
|
|
m_LuaState = nullptr;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::AddPackagePath(const AString & a_PathVariable, const AString & a_Path)
|
|
{
|
|
ASSERT_LUA_STACK_BALANCE(m_LuaState);
|
|
|
|
// 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); // Stk: -
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::LoadFile(const AString & a_FileName, bool a_LogWarnings)
|
|
{
|
|
ASSERT(IsValid());
|
|
ASSERT_LUA_STACK_BALANCE(m_LuaState);
|
|
|
|
// Load the file:
|
|
int s = luaL_loadfile(m_LuaState, a_FileName.c_str());
|
|
if (s != 0)
|
|
{
|
|
if (a_LogWarnings)
|
|
{
|
|
LOGWARNING("Can't load %s because of a load error in file %s: %d (%s)", m_SubsystemName.c_str(), a_FileName.c_str(), s, lua_tostring(m_LuaState, -1));
|
|
}
|
|
lua_pop(m_LuaState, 1);
|
|
return false;
|
|
}
|
|
|
|
// Execute the globals:
|
|
s = lua_pcall(m_LuaState, 0, 0, 0);
|
|
if (s != 0)
|
|
{
|
|
if (a_LogWarnings)
|
|
{
|
|
LOGWARNING("Can't load %s because of an initialization error in file %s: %d (%s)", m_SubsystemName.c_str(), a_FileName.c_str(), s, lua_tostring(m_LuaState, -1));
|
|
}
|
|
lua_pop(m_LuaState, 1);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::LoadString(const AString & a_StringToLoad, const AString & a_FileName, bool a_LogWarnings)
|
|
{
|
|
ASSERT(IsValid());
|
|
ASSERT_LUA_STACK_BALANCE(m_LuaState);
|
|
|
|
// Load the file:
|
|
int s = luaL_loadstring(m_LuaState, a_StringToLoad.c_str());
|
|
if (s != 0)
|
|
{
|
|
if (a_LogWarnings)
|
|
{
|
|
LOGWARNING("Can't load %s because of a load error in string from \"%s\": %d (%s)", m_SubsystemName.c_str(), a_FileName.c_str(), s, lua_tostring(m_LuaState, -1));
|
|
}
|
|
lua_pop(m_LuaState, 1);
|
|
return false;
|
|
}
|
|
|
|
// Execute the globals:
|
|
s = lua_pcall(m_LuaState, 0, LUA_MULTRET, 0);
|
|
if (s != 0)
|
|
{
|
|
if (a_LogWarnings)
|
|
{
|
|
LOGWARNING("Can't load %s because of an initialization error in string from \"%s\": %d (%s)", m_SubsystemName.c_str(), a_FileName.c_str(), s, lua_tostring(m_LuaState, -1));
|
|
}
|
|
lua_pop(m_LuaState, 1);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::HasFunction(const char * a_FunctionName)
|
|
{
|
|
if (!IsValid())
|
|
{
|
|
// This happens if cPlugin::Initialize() fails with an error
|
|
return false;
|
|
}
|
|
|
|
ASSERT_LUA_STACK_BALANCE(m_LuaState);
|
|
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)
|
|
{
|
|
ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack
|
|
|
|
if (!IsValid())
|
|
{
|
|
// This happens if cPlugin::Initialize() fails with an error
|
|
return false;
|
|
}
|
|
|
|
// Push the error handler for lua_pcall()
|
|
lua_pushcfunction(m_LuaState, &ReportFnCallErrors);
|
|
|
|
lua_getglobal(m_LuaState, a_FunctionName);
|
|
if (!lua_isfunction(m_LuaState, -1))
|
|
{
|
|
LOGWARNING("Error in %s: Could not find function %s()", m_SubsystemName.c_str(), a_FunctionName);
|
|
lua_pop(m_LuaState, 2);
|
|
return false;
|
|
}
|
|
m_CurrentFunctionName.assign(a_FunctionName);
|
|
m_NumCurrentFunctionArgs = 0;
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::PushFunction(const cRef & a_FnRef)
|
|
{
|
|
ASSERT(IsValid());
|
|
ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack
|
|
|
|
// Push the error handler for lua_pcall()
|
|
lua_pushcfunction(m_LuaState, &ReportFnCallErrors);
|
|
|
|
lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, static_cast<int>(a_FnRef)); // same as lua_getref()
|
|
if (!lua_isfunction(m_LuaState, -1))
|
|
{
|
|
lua_pop(m_LuaState, 2);
|
|
return false;
|
|
}
|
|
m_CurrentFunctionName = "<callback>";
|
|
m_NumCurrentFunctionArgs = 0;
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::PushFunction(const cRef & a_TableRef, const char * a_FnName)
|
|
{
|
|
ASSERT(IsValid());
|
|
ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack
|
|
|
|
if (!a_TableRef.IsValid())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Push the error handler for lua_pcall()
|
|
lua_pushcfunction(m_LuaState, &ReportFnCallErrors);
|
|
|
|
// Get the function from the table:
|
|
lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, static_cast<int>(a_TableRef));
|
|
if (!lua_istable(m_LuaState, -1))
|
|
{
|
|
// Not a table, bail out
|
|
lua_pop(m_LuaState, 2);
|
|
return false;
|
|
}
|
|
lua_getfield(m_LuaState, -1, a_FnName);
|
|
if (lua_isnil(m_LuaState, -1) || !lua_isfunction(m_LuaState, -1))
|
|
{
|
|
// Not a valid function, bail out
|
|
lua_pop(m_LuaState, 3);
|
|
return false;
|
|
}
|
|
|
|
// Pop the table off the stack:
|
|
lua_remove(m_LuaState, -2);
|
|
|
|
Printf(m_CurrentFunctionName, "<table-callback %s>", a_FnName);
|
|
m_NumCurrentFunctionArgs = 0;
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(const AString & a_String)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
lua_pushlstring(m_LuaState, a_String.data(), a_String.size());
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(const AStringMap & a_Dictionary)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
lua_createtable(m_LuaState, 0, static_cast<int>(a_Dictionary.size()));
|
|
int newTable = lua_gettop(m_LuaState);
|
|
for (const auto & item: a_Dictionary)
|
|
{
|
|
Push(item.first); // key
|
|
Push(item.second); // value
|
|
lua_rawset(m_LuaState, newTable);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(const AStringVector & a_Vector)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
lua_createtable(m_LuaState, static_cast<int>(a_Vector.size()), 0);
|
|
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);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(const cCraftingGrid * a_Grid)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<cCraftingGrid *>(a_Grid)), "cCraftingGrid");
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(const cCraftingRecipe * a_Recipe)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<cCraftingRecipe *>(a_Recipe)), "cCraftingRecipe");
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(const char * a_Value)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
tolua_pushstring(m_LuaState, a_Value);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(const cItems & a_Items)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<cItems *>(&a_Items)), "cItems");
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(const cNil & a_Nil)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
lua_pushnil(m_LuaState);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(const cPlayer * a_Player)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<cPlayer *>(a_Player)), "cPlayer");
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(const cLuaState::cRef & a_Ref)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, static_cast<int>(a_Ref));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(const HTTPRequest * a_Request)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<HTTPRequest *>(a_Request)), "HTTPRequest");
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(const HTTPTemplateRequest * a_Request)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<HTTPTemplateRequest *>(a_Request)), "HTTPTemplateRequest");
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(const Vector3d & a_Vector)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<Vector3d *>(&a_Vector)), "Vector3<double>");
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(const Vector3d * a_Vector)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<Vector3d *>(a_Vector)), "Vector3<double>");
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(const Vector3i & a_Vector)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<Vector3i *>(&a_Vector)), "Vector3<int>");
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(const Vector3i * a_Vector)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
tolua_pushusertype(m_LuaState, reinterpret_cast<void *>(const_cast<Vector3i *>(a_Vector)), "Vector3<int>");
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(bool a_Value)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
tolua_pushboolean(m_LuaState, a_Value ? 1 : 0);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(cEntity * a_Entity)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
if (a_Entity == nullptr)
|
|
{
|
|
lua_pushnil(m_LuaState);
|
|
}
|
|
else
|
|
{
|
|
switch (a_Entity->GetEntityType())
|
|
{
|
|
case cEntity::etMonster:
|
|
{
|
|
// Don't push specific mob types, as those are not exported in the API:
|
|
tolua_pushusertype(m_LuaState, a_Entity, "cMonster");
|
|
break;
|
|
}
|
|
case cEntity::etPlayer:
|
|
{
|
|
tolua_pushusertype(m_LuaState, a_Entity, "cPlayer");
|
|
break;
|
|
}
|
|
case cEntity::etPickup:
|
|
{
|
|
tolua_pushusertype(m_LuaState, a_Entity, "cPickup");
|
|
break;
|
|
}
|
|
case cEntity::etTNT:
|
|
{
|
|
tolua_pushusertype(m_LuaState, a_Entity, "cTNTEntity");
|
|
break;
|
|
}
|
|
case cEntity::etProjectile:
|
|
{
|
|
tolua_pushusertype(m_LuaState, a_Entity, a_Entity->GetClass());
|
|
break;
|
|
}
|
|
case cEntity::etFloater:
|
|
{
|
|
tolua_pushusertype(m_LuaState, a_Entity, "cFloater");
|
|
break;
|
|
}
|
|
|
|
case cEntity::etEntity:
|
|
case cEntity::etEnderCrystal:
|
|
case cEntity::etFallingBlock:
|
|
case cEntity::etMinecart:
|
|
case cEntity::etBoat:
|
|
case cEntity::etExpOrb:
|
|
case cEntity::etItemFrame:
|
|
case cEntity::etPainting:
|
|
{
|
|
// Push the generic entity class type:
|
|
tolua_pushusertype(m_LuaState, a_Entity, "cEntity");
|
|
}
|
|
} // switch (EntityType)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(cLuaServerHandle * a_ServerHandle)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
tolua_pushusertype(m_LuaState, a_ServerHandle, "cServerHandle");
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(cLuaTCPLink * a_TCPLink)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
tolua_pushusertype(m_LuaState, a_TCPLink, "cTCPLink");
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(cLuaUDPEndpoint * a_UDPEndpoint)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
tolua_pushusertype(m_LuaState, a_UDPEndpoint, "cUDPEndpoint");
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(double a_Value)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
tolua_pushnumber(m_LuaState, a_Value);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(int a_Value)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
tolua_pushnumber(m_LuaState, a_Value);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(long a_Value)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
tolua_pushnumber(m_LuaState, static_cast<lua_Number>(a_Value));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(UInt32 a_Value)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
tolua_pushnumber(m_LuaState, a_Value);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Push(std::chrono::milliseconds a_Value)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
tolua_pushnumber(m_LuaState, static_cast<lua_Number>(a_Value.count()));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::Pop(int a_NumValuesToPop)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
lua_pop(m_LuaState, a_NumValuesToPop);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::GetStackValue(int a_StackPos, AString & a_Value)
|
|
{
|
|
size_t len = 0;
|
|
const char * data = lua_tolstring(m_LuaState, a_StackPos, &len);
|
|
if (data != nullptr)
|
|
{
|
|
a_Value.assign(data, len);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::GetStackValue(int a_StackPos, AStringMap & a_Value)
|
|
{
|
|
// Retrieve all values in a string => string dictionary table:
|
|
if (!lua_istable(m_LuaState, a_StackPos))
|
|
{
|
|
return false;
|
|
}
|
|
cStackTable tbl(*this, a_StackPos);
|
|
bool isValid = true;
|
|
tbl.ForEachElement([&isValid, &a_Value](cLuaState & a_LuaState)
|
|
{
|
|
AString key, val;
|
|
if (a_LuaState.GetStackValues(-2, key, val))
|
|
{
|
|
a_Value[key] = val;
|
|
}
|
|
else
|
|
{
|
|
isValid = false;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
);
|
|
return isValid;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::GetStackValue(int a_StackPos, bool & a_ReturnedVal)
|
|
{
|
|
a_ReturnedVal = (tolua_toboolean(m_LuaState, a_StackPos, a_ReturnedVal ? 1 : 0) > 0);
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::GetStackValue(int a_StackPos, cCallback & a_Callback)
|
|
{
|
|
return a_Callback.RefStack(*this, a_StackPos);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::GetStackValue(int a_StackPos, cCallbackPtr & a_Callback)
|
|
{
|
|
if (a_Callback == nullptr)
|
|
{
|
|
a_Callback = cpp14::make_unique<cCallback>();
|
|
}
|
|
return a_Callback->RefStack(*this, a_StackPos);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::GetStackValue(int a_StackPos, cOptionalCallback & a_Callback)
|
|
{
|
|
return a_Callback.RefStack(*this, a_StackPos);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::GetStackValue(int a_StackPos, cOptionalCallbackPtr & a_Callback)
|
|
{
|
|
if (a_Callback == nullptr)
|
|
{
|
|
a_Callback = cpp14::make_unique<cOptionalCallback>();
|
|
}
|
|
return a_Callback->RefStack(*this, a_StackPos);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::GetStackValue(int a_StackPos, cCallbackSharedPtr & a_Callback)
|
|
{
|
|
if (a_Callback == nullptr)
|
|
{
|
|
a_Callback = std::make_shared<cCallback>();
|
|
}
|
|
return a_Callback->RefStack(*this, a_StackPos);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::GetStackValue(int a_StackPos, cPluginManager::CommandResult & a_Result)
|
|
{
|
|
if (lua_isnumber(m_LuaState, a_StackPos))
|
|
{
|
|
a_Result = static_cast<cPluginManager::CommandResult>(static_cast<int>((tolua_tonumber(m_LuaState, a_StackPos, a_Result))));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::GetStackValue(int a_StackPos, cRef & a_Ref)
|
|
{
|
|
a_Ref.RefStack(*this, a_StackPos);
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::GetStackValue(int a_StackPos, cStackTablePtr & a_StackTable)
|
|
{
|
|
// Only allow tables to be stored in a_StackTable:
|
|
if (!lua_istable(m_LuaState, a_StackPos))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Assign the StackTable to the specified stack position:
|
|
a_StackTable = cpp14::make_unique<cStackTable>(*this, a_StackPos);
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::GetStackValue(int a_StackPos, cTableRef & a_TableRef)
|
|
{
|
|
return a_TableRef.RefStack(*this, a_StackPos);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::GetStackValue(int a_StackPos, cTableRefPtr & a_TableRef)
|
|
{
|
|
if (a_TableRef == nullptr)
|
|
{
|
|
a_TableRef = cpp14::make_unique<cTableRef>();
|
|
}
|
|
return a_TableRef->RefStack(*this, a_StackPos);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::GetStackValue(int a_StackPos, cTrackedRef & a_Ref)
|
|
{
|
|
return a_Ref.RefStack(*this, a_StackPos);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::GetStackValue(int a_StackPos, cTrackedRefPtr & a_Ref)
|
|
{
|
|
if (a_Ref == nullptr)
|
|
{
|
|
a_Ref = cpp14::make_unique<cTrackedRef>();
|
|
}
|
|
return a_Ref->RefStack(*this, a_StackPos);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::GetStackValue(int a_StackPos, cTrackedRefSharedPtr & a_Ref)
|
|
{
|
|
if (a_Ref == nullptr)
|
|
{
|
|
a_Ref = std::make_shared<cTrackedRef>();
|
|
}
|
|
return a_Ref->RefStack(*this, a_StackPos);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::GetStackValue(int a_StackPos, double & a_ReturnedVal)
|
|
{
|
|
if (lua_isnumber(m_LuaState, a_StackPos))
|
|
{
|
|
a_ReturnedVal = tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::GetStackValue(int a_StackPos, eBlockFace & a_ReturnedVal)
|
|
{
|
|
if (!lua_isnumber(m_LuaState, a_StackPos))
|
|
{
|
|
return false;
|
|
}
|
|
a_ReturnedVal = static_cast<eBlockFace>(Clamp(
|
|
static_cast<int>(tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal)),
|
|
static_cast<int>(BLOCK_FACE_MIN), static_cast<int>(BLOCK_FACE_MAX))
|
|
);
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::GetStackValue(int a_StackPos, eWeather & a_ReturnedVal)
|
|
{
|
|
if (!lua_isnumber(m_LuaState, a_StackPos))
|
|
{
|
|
return false;
|
|
}
|
|
a_ReturnedVal = static_cast<eWeather>(Clamp(
|
|
static_cast<int>(tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal)),
|
|
static_cast<int>(wSunny), static_cast<int>(wThunderstorm))
|
|
);
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::GetStackValue(int a_StackPos, float & a_ReturnedVal)
|
|
{
|
|
if (lua_isnumber(m_LuaState, a_StackPos))
|
|
{
|
|
a_ReturnedVal = static_cast<float>(tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cLuaState::cStackValue cLuaState::WalkToValue(const AString & a_Name)
|
|
{
|
|
// There needs to be at least one value on the stack:
|
|
ASSERT(lua_gettop(m_LuaState) > 0);
|
|
|
|
// Iterate over path and replace the top of the stack with the walked element
|
|
lua_pushvalue(m_LuaState, -1); // Copy the stack value into the "working area"
|
|
auto path = StringSplit(a_Name, ".");
|
|
for (const auto & elem: path)
|
|
{
|
|
// If the value is not a table, bail out (error):
|
|
if (!lua_istable(m_LuaState, -1))
|
|
{
|
|
lua_pop(m_LuaState, 1);
|
|
return cStackValue();
|
|
}
|
|
|
|
// Get the next part of the path:
|
|
lua_getfield(m_LuaState, -1, elem.c_str());
|
|
|
|
// Remove the previous value from the stack (keep only the new one):
|
|
lua_remove(m_LuaState, -2);
|
|
} // for elem - path[]
|
|
if (lua_isnil(m_LuaState, -1))
|
|
{
|
|
lua_pop(m_LuaState, 1);
|
|
return cStackValue();
|
|
}
|
|
return cStackValue(*this);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cLuaState::cStackValue cLuaState::WalkToNamedGlobal(const AString & a_Name)
|
|
{
|
|
// Iterate over path and replace the top of the stack with the walked element
|
|
lua_getglobal(m_LuaState, "_G");
|
|
auto path = StringSplit(a_Name, ".");
|
|
for (const auto & elem: path)
|
|
{
|
|
// If the value is not a table, bail out (error):
|
|
if (!lua_istable(m_LuaState, -1))
|
|
{
|
|
lua_pop(m_LuaState, 1);
|
|
return cStackValue();
|
|
}
|
|
|
|
// Get the next part of the path:
|
|
lua_getfield(m_LuaState, -1, elem.c_str());
|
|
|
|
// Remove the previous value from the stack (keep only the new one):
|
|
lua_remove(m_LuaState, -2);
|
|
} // for elem - path[]
|
|
if (lua_isnil(m_LuaState, -1))
|
|
{
|
|
lua_pop(m_LuaState, 1);
|
|
return cStackValue();
|
|
}
|
|
return cStackValue(*this);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::CallFunction(int a_NumResults)
|
|
{
|
|
ASSERT (m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first
|
|
ASSERT(lua_isfunction(m_LuaState, -m_NumCurrentFunctionArgs - 1)); // The function to call
|
|
ASSERT(lua_isfunction(m_LuaState, -m_NumCurrentFunctionArgs - 2)); // The error handler
|
|
|
|
// 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);
|
|
if (s != 0)
|
|
{
|
|
// The error has already been printed together with the stacktrace
|
|
LOGWARNING("Error in %s calling function %s()", m_SubsystemName.c_str(), CurrentFunctionName.c_str());
|
|
|
|
// Remove the error handler and error message from the stack:
|
|
ASSERT(lua_gettop(m_LuaState) == 2);
|
|
lua_pop(m_LuaState, 2);
|
|
return false;
|
|
}
|
|
|
|
// Remove the error handler from the stack:
|
|
lua_remove(m_LuaState, -a_NumResults - 1);
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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 != nullptr) ? entry.name : "?");
|
|
tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
|
|
return false;
|
|
} // for i - Param
|
|
|
|
// All params checked ok
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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 != nullptr) ? 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 != nullptr) ? entry.name : "?");
|
|
|
|
BreakIntoDebugger(m_LuaState);
|
|
|
|
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 != nullptr) ? entry.name : "?");
|
|
tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
|
|
return false;
|
|
} // for i - Param
|
|
|
|
// All params checked ok
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::CheckParamBool(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_isboolean(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 != nullptr) ? entry.name : "?");
|
|
tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
|
|
return false;
|
|
} // for i - Param
|
|
|
|
// All params checked ok
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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 (lua_isstring(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));
|
|
tolua_err.array = 0;
|
|
tolua_err.type = "string";
|
|
tolua_err.index = i;
|
|
AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != nullptr) ? entry.name : "?");
|
|
tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
|
|
return false;
|
|
} // for i - Param
|
|
|
|
// All params checked ok
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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));
|
|
luaL_error(m_LuaState, "Error in function '%s' parameter #%d. Function expected, got %s",
|
|
(entry.name != nullptr) ? 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",
|
|
(entry.name != nullptr) ? entry.name : "?", i, GetTypeText(i).c_str()
|
|
);
|
|
return false;
|
|
} // for i - Param
|
|
|
|
// All params checked ok
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::CheckParamEnd(int a_Param)
|
|
{
|
|
tolua_Error tolua_err;
|
|
if (tolua_isnoobj(m_LuaState, a_Param, &tolua_err) == 1)
|
|
{
|
|
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 != nullptr) ? entry.name : "?");
|
|
tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::IsParamUserType(int a_Param, AString a_UserType)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
tolua_Error tolua_err;
|
|
return (tolua_isusertype(m_LuaState, a_Param, a_UserType.c_str(), 0, &tolua_err) == 1);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::IsParamNumber(int a_Param)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
tolua_Error tolua_err;
|
|
return (tolua_isnumber(m_LuaState, a_Param, 0, &tolua_err) == 1);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::LogStackTrace(int a_StartingDepth)
|
|
{
|
|
LogStackTrace(m_LuaState, a_StartingDepth);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::LogStackTrace(lua_State * a_LuaState, int a_StartingDepth)
|
|
{
|
|
LOGWARNING("Stack trace:");
|
|
lua_Debug entry;
|
|
int depth = a_StartingDepth;
|
|
while (lua_getstack(a_LuaState, depth, &entry))
|
|
{
|
|
lua_getinfo(a_LuaState, "Sln", &entry);
|
|
LOGWARNING(" %s(%d): %s", entry.short_src, entry.currentline, entry.name ? entry.name : "(no name)");
|
|
depth++;
|
|
}
|
|
LOGWARNING("Stack trace end");
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
AString cLuaState::GetTypeText(int a_StackPos)
|
|
{
|
|
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_settop(m_LuaState, OldTop);
|
|
return -1;
|
|
}
|
|
|
|
// Copy the function parameters to the target state
|
|
if (CopyStackFrom(a_SrcLuaState, a_SrcParamStart, a_SrcParamEnd) < 0)
|
|
{
|
|
// Something went wrong, fix the stack and exit
|
|
lua_settop(m_LuaState, OldTop);
|
|
m_NumCurrentFunctionArgs = -1;
|
|
m_CurrentFunctionName.clear();
|
|
return -1;
|
|
}
|
|
|
|
// Call the function, with an error handler:
|
|
int s = lua_pcall(m_LuaState, a_SrcParamEnd - a_SrcParamStart + 1, LUA_MULTRET, OldTop + 1);
|
|
if (ReportErrors(s))
|
|
{
|
|
LOGWARN("Error while calling function '%s' in '%s'", a_FunctionName.c_str(), m_SubsystemName.c_str());
|
|
// Reset the stack:
|
|
lua_settop(m_LuaState, OldTop);
|
|
|
|
// Reset the internal checking mechanisms:
|
|
m_NumCurrentFunctionArgs = -1;
|
|
m_CurrentFunctionName.clear();
|
|
|
|
// 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;
|
|
}
|
|
|
|
// Reset the internal checking mechanisms:
|
|
m_NumCurrentFunctionArgs = -1;
|
|
m_CurrentFunctionName.clear();
|
|
|
|
// 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, int a_NumAllowedNestingLevels)
|
|
{
|
|
/*
|
|
// 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)
|
|
{
|
|
if (!CopySingleValueFrom(a_SrcLuaState, i, a_NumAllowedNestingLevels))
|
|
{
|
|
lua_pop(m_LuaState, i - a_SrcStart);
|
|
return -1;
|
|
}
|
|
}
|
|
return a_SrcEnd - a_SrcStart + 1;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::CopyTableFrom(cLuaState & a_SrcLuaState, int a_SrcStackIdx, int a_NumAllowedNestingLevels)
|
|
{
|
|
// Create the dest table:
|
|
#ifdef _DEBUG
|
|
auto srcTop = lua_gettop(a_SrcLuaState);
|
|
auto dstTop = lua_gettop(m_LuaState);
|
|
#endif
|
|
lua_createtable(m_LuaState, 0, 0); // DST: <table>
|
|
lua_pushvalue(a_SrcLuaState, a_SrcStackIdx); // SRC: <table>
|
|
lua_pushnil(a_SrcLuaState); // SRC: <table> <key>
|
|
while (lua_next(a_SrcLuaState, -2) != 0) // SRC: <table> <key> <value>
|
|
{
|
|
ASSERT(lua_gettop(a_SrcLuaState) == srcTop + 3);
|
|
ASSERT(lua_gettop(m_LuaState) == dstTop + 1);
|
|
|
|
// Copy the key:
|
|
if (!CopySingleValueFrom(a_SrcLuaState, -2, a_NumAllowedNestingLevels)) // DST: <table> <key>
|
|
{
|
|
lua_pop(m_LuaState, 1);
|
|
lua_pop(a_SrcLuaState, 3);
|
|
ASSERT(lua_gettop(a_SrcLuaState) == srcTop);
|
|
ASSERT(lua_gettop(m_LuaState) == dstTop);
|
|
return false;
|
|
}
|
|
ASSERT(lua_gettop(a_SrcLuaState) == srcTop + 3);
|
|
ASSERT(lua_gettop(m_LuaState) == dstTop + 2);
|
|
|
|
// Copy the value:
|
|
if (!CopySingleValueFrom(a_SrcLuaState, -1, a_NumAllowedNestingLevels - 1)) // DST: <table> <key> <value>
|
|
{
|
|
lua_pop(m_LuaState, 2); // DST: empty
|
|
lua_pop(a_SrcLuaState, 3); // SRC: empty
|
|
ASSERT(lua_gettop(a_SrcLuaState) == srcTop);
|
|
ASSERT(lua_gettop(m_LuaState) == dstTop);
|
|
return false;
|
|
}
|
|
ASSERT(lua_gettop(a_SrcLuaState) == srcTop + 3);
|
|
ASSERT(lua_gettop(m_LuaState) == dstTop + 3);
|
|
|
|
// Set the value and fix up stacks:
|
|
lua_rawset(m_LuaState, -3); // DST: <table>
|
|
lua_pop(a_SrcLuaState, 1); // SRC: <table> <key>
|
|
ASSERT(lua_gettop(a_SrcLuaState) == srcTop + 2);
|
|
ASSERT(lua_gettop(m_LuaState) == dstTop + 1);
|
|
}
|
|
lua_pop(a_SrcLuaState, 1); // SRC: empty
|
|
ASSERT(lua_gettop(a_SrcLuaState) == srcTop);
|
|
ASSERT(lua_gettop(m_LuaState) == dstTop + 1);
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cLuaState::CopySingleValueFrom(cLuaState & a_SrcLuaState, int a_StackIdx, int a_NumAllowedNestingLevels)
|
|
{
|
|
int t = lua_type(a_SrcLuaState, a_StackIdx);
|
|
switch (t)
|
|
{
|
|
case LUA_TNIL:
|
|
{
|
|
lua_pushnil(m_LuaState);
|
|
return true;
|
|
}
|
|
case LUA_TSTRING:
|
|
{
|
|
AString s;
|
|
a_SrcLuaState.ToString(a_StackIdx, s);
|
|
Push(s);
|
|
return true;
|
|
}
|
|
case LUA_TBOOLEAN:
|
|
{
|
|
bool b = (tolua_toboolean(a_SrcLuaState, a_StackIdx, false) != 0);
|
|
Push(b);
|
|
return true;
|
|
}
|
|
case LUA_TNUMBER:
|
|
{
|
|
lua_Number d = tolua_tonumber(a_SrcLuaState, a_StackIdx, 0);
|
|
Push(d);
|
|
return true;
|
|
}
|
|
case LUA_TUSERDATA:
|
|
{
|
|
// Get the class name:
|
|
const char * type = nullptr;
|
|
if (lua_getmetatable(a_SrcLuaState, a_StackIdx) == 0)
|
|
{
|
|
LOGWARNING("%s: Unknown class in pos %d, cannot copy.", __FUNCTION__, a_StackIdx);
|
|
return false;
|
|
}
|
|
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, a_StackIdx, nullptr);
|
|
tolua_pushusertype(m_LuaState, ud, type);
|
|
return true;
|
|
}
|
|
case LUA_TTABLE:
|
|
{
|
|
if (!CopyTableFrom(a_SrcLuaState, a_StackIdx, a_NumAllowedNestingLevels - 1))
|
|
{
|
|
LOGWARNING("%s: Failed to copy table in pos %d.", __FUNCTION__, a_StackIdx);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
default:
|
|
{
|
|
LOGWARNING("%s: Unsupported value: '%s' at stack position %d. Can only copy numbers, strings, bools, classes and simple tables!",
|
|
__FUNCTION__, lua_typename(a_SrcLuaState, t), a_StackIdx
|
|
);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::ToString(int a_StackPos, AString & a_String)
|
|
{
|
|
size_t len;
|
|
const char * s = lua_tolstring(m_LuaState, a_StackPos, &len);
|
|
if (s != nullptr)
|
|
{
|
|
a_String.assign(s, len);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::LogStackValues(const char * a_Header)
|
|
{
|
|
LogStackValues(m_LuaState, a_Header);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::LogStackValues(lua_State * a_LuaState, const char * a_Header)
|
|
{
|
|
// Format string consisting only of %s is used to appease the compiler
|
|
ASSERT_LUA_STACK_BALANCE(a_LuaState, false);
|
|
LOG("%s", (a_Header != nullptr) ? a_Header : "Lua C API Stack contents:");
|
|
for (int i = lua_gettop(a_LuaState); i > 0; i--)
|
|
{
|
|
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", static_cast<double>(lua_tonumber(a_LuaState, i))); break;
|
|
case LUA_TSTRING:
|
|
{
|
|
size_t len;
|
|
const char * txt = lua_tolstring(a_LuaState, i, &len);
|
|
Value.assign(txt, std::min<size_t>(len, 50)); // Only log up to 50 characters of the string
|
|
break;
|
|
}
|
|
case LUA_TTABLE: Printf(Value, "%p", lua_topointer(a_LuaState, i)); break;
|
|
case LUA_TFUNCTION: Printf(Value, "%p", lua_topointer(a_LuaState, i)); break;
|
|
case LUA_TUSERDATA:
|
|
{
|
|
Printf(Value, "%p (%s)", lua_touserdata(a_LuaState, i), tolua_typename(a_LuaState, i));
|
|
// tolua_typename pushes the string onto Lua stack, pop it off again:
|
|
lua_pop(a_LuaState, 1);
|
|
break;
|
|
}
|
|
default: break;
|
|
}
|
|
LOGD(" Idx %d: type %d (%s) %s", i, Type, lua_typename(a_LuaState, Type), Value.c_str());
|
|
} // for i - stack idx
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cLuaState * cLuaState::QueryCanonLuaState(void) const
|
|
{
|
|
return cCanonLuaStates::GetCanonState(m_LuaState);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::LogApiCallParamFailure(const char * a_FnName, const char * a_ParamNames)
|
|
{
|
|
LOGWARNING("%s: Cannot read params: %s, bailing out.", a_FnName, a_ParamNames);
|
|
LogStackTrace();
|
|
LogStackValues("Values on the stack");
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int cLuaState::ReportFnCallErrors(lua_State * a_LuaState)
|
|
{
|
|
LOGWARNING("LUA: %s", lua_tostring(a_LuaState, -1));
|
|
LogStackTrace(a_LuaState, 1);
|
|
BreakIntoDebugger(a_LuaState);
|
|
return 1; // We left the error message on the stack as the return value
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int cLuaState::BreakIntoDebugger(lua_State * a_LuaState)
|
|
{
|
|
ASSERT_LUA_STACK_BALANCE(a_LuaState);
|
|
// Call the BreakIntoDebugger function, if available:
|
|
lua_getglobal(a_LuaState, "BreakIntoDebugger");
|
|
if (!lua_isfunction(a_LuaState, -1))
|
|
{
|
|
LOGD("LUA: BreakIntoDebugger() not found / not a function");
|
|
lua_pop(a_LuaState, 1);
|
|
return 1;
|
|
}
|
|
lua_pushvalue(a_LuaState, -2); // Copy the string that has been passed to us
|
|
LOGD("Calling BreakIntoDebugger()...");
|
|
lua_call(a_LuaState, 1, 0);
|
|
LOGD("Returned from BreakIntoDebugger().");
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::TrackRef(cTrackedRef & a_Ref)
|
|
{
|
|
// Get the CanonLuaState global from Lua:
|
|
auto canonState = QueryCanonLuaState();
|
|
if (canonState == nullptr)
|
|
{
|
|
LOGWARNING("%s: Lua state %p has invalid CanonLuaState!", __FUNCTION__, reinterpret_cast<void *>(m_LuaState));
|
|
return;
|
|
}
|
|
|
|
// Add the callback:
|
|
cCSLock Lock(canonState->m_CSTrackedRefs);
|
|
canonState->m_TrackedRefs.push_back(&a_Ref);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::UntrackRef(cTrackedRef & a_Ref)
|
|
{
|
|
// Get the CanonLuaState global from Lua:
|
|
auto canonState = QueryCanonLuaState();
|
|
if (canonState == nullptr)
|
|
{
|
|
LOGWARNING("%s: Lua state %p has invalid CanonLuaState!", __FUNCTION__, reinterpret_cast<void *>(m_LuaState));
|
|
return;
|
|
}
|
|
|
|
// Remove the callback (note that another thread may have cleared the callbacks by closing the LuaState):
|
|
cCSLock Lock(canonState->m_CSTrackedRefs);
|
|
auto & trackedRefs = canonState->m_TrackedRefs;
|
|
for (auto itr = trackedRefs.begin(), end = trackedRefs.end(); itr != end; ++itr)
|
|
{
|
|
if (*itr == &a_Ref)
|
|
{
|
|
trackedRefs.erase(itr);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// cLuaState::cRef:
|
|
|
|
cLuaState::cRef::cRef(void) :
|
|
m_LuaState(nullptr),
|
|
m_Ref(LUA_REFNIL)
|
|
{
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cLuaState::cRef::cRef(cLuaState & a_LuaState, int a_StackPos) :
|
|
m_LuaState(nullptr),
|
|
m_Ref(LUA_REFNIL)
|
|
{
|
|
RefStack(a_LuaState, a_StackPos);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cLuaState::cRef::cRef(cRef && a_FromRef):
|
|
m_LuaState(a_FromRef.m_LuaState),
|
|
m_Ref(a_FromRef.m_Ref)
|
|
{
|
|
a_FromRef.m_LuaState = nullptr;
|
|
a_FromRef.m_Ref = LUA_REFNIL;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cLuaState::cRef::~cRef()
|
|
{
|
|
if (m_LuaState != nullptr)
|
|
{
|
|
UnRef();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cLuaState::cRef::RefStack(cLuaState & a_LuaState, int a_StackPos)
|
|
{
|
|
ASSERT(a_LuaState.IsValid());
|
|
if (m_LuaState != nullptr)
|
|
{
|
|
UnRef();
|
|
}
|
|
ASSERT(cCanonLuaStates::GetCanonState(a_LuaState)->m_CS.IsLockedByCurrentThread());
|
|
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)
|
|
{
|
|
if (IsValid())
|
|
{
|
|
ASSERT(cCanonLuaStates::GetCanonState(m_LuaState)->m_CS.IsLockedByCurrentThread());
|
|
luaL_unref(m_LuaState, LUA_REGISTRYINDEX, m_Ref);
|
|
}
|
|
m_LuaState = nullptr;
|
|
m_Ref = LUA_REFNIL;
|
|
}
|
|
|
|
|
|
|
|
|