summaryrefslogtreecommitdiffstats
path: root/src/Bindings/LuaState.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Bindings/LuaState.cpp')
-rw-r--r--src/Bindings/LuaState.cpp273
1 files changed, 264 insertions, 9 deletions
diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp
index f11e74e75..5e6c24365 100644
--- a/src/Bindings/LuaState.cpp
+++ b/src/Bindings/LuaState.cpp
@@ -20,6 +20,10 @@ extern "C"
#include "../Entities/Entity.h"
#include "../BlockEntities/BlockEntity.h"
+
+
+
+
// fwd: "SQLite/lsqlite3.c"
extern "C"
{
@@ -39,6 +43,10 @@ extern "C"
const cLuaState::cRet cLuaState::Return = {};
+/** Each Lua state stores a pointer to its creating cLuaState in Lua globals, under this name.
+This way any cLuaState can reference the main cLuaState's TrackedCallbacks, mutex etc. */
+static const char * g_CanonLuaStateGlobalName = "_CuberiteInternal_CanonLuaState";
+
@@ -114,6 +122,135 @@ cLuaStateTracker & cLuaStateTracker::Get(void)
////////////////////////////////////////////////////////////////////////////////
+// cLuaState::cCallback:
+
+cLuaState::cCallback::cCallback(void):
+ m_CS(nullptr)
+{
+}
+
+
+
+
+
+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;
+ }
+
+ // Clear any previous callback:
+ Clear();
+
+ // Add self to LuaState's callback-tracking:
+ auto canonState = a_LuaState.QueryCanonLuaState();
+ canonState->TrackCallback(*this);
+
+ // Store the new callback:
+ m_CS = &(canonState->m_CS);
+ m_Ref.RefStack(*canonState, a_StackPos);
+ return true;
+}
+
+
+
+
+
+void cLuaState::cCallback::Clear(void)
+{
+ // Free the callback reference:
+ lua_State * luaState = nullptr;
+ {
+ auto cs = m_CS;
+ if (cs != nullptr)
+ {
+ cCSLock Lock(*cs);
+ if (!m_Ref.IsValid())
+ {
+ return;
+ }
+ luaState = m_Ref.GetLuaState();
+ m_Ref.UnRef();
+ }
+ }
+
+ // Remove from LuaState's callback-tracking:
+ if (luaState == nullptr)
+ {
+ return;
+ }
+ cLuaState(luaState).UntrackCallback(*this);
+}
+
+
+
+
+
+bool cLuaState::cCallback::IsValid(void)
+{
+ auto cs = m_CS;
+ if (cs == nullptr)
+ {
+ return false;
+ }
+ cCSLock lock(*cs);
+ return m_Ref.IsValid();
+}
+
+
+
+
+
+bool cLuaState::cCallback::IsSameLuaState(cLuaState & a_LuaState)
+{
+ auto cs = m_CS;
+ 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::cCallback::Invalidate(void)
+{
+ auto cs = m_CS;
+ 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();
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
// cLuaState:
cLuaState::cLuaState(const AString & a_SubsystemName) :
@@ -170,6 +307,10 @@ void cLuaState::Create(void)
luaL_openlibs(m_LuaState);
m_IsOwned = true;
cLuaStateTracker::Add(*this);
+
+ // Add the CanonLuaState value into the Lua state, so that we can get it from anywhere:
+ lua_pushlightuserdata(m_LuaState, reinterpret_cast<void *>(this));
+ lua_setglobal(m_LuaState, g_CanonLuaStateGlobalName);
}
@@ -206,6 +347,16 @@ void cLuaState::Close(void)
Detach();
return;
}
+
+ // Invalidate all callbacks:
+ {
+ cCSLock Lock(m_CSTrackedCallbacks);
+ for (auto & c: m_TrackedCallbacks)
+ {
+ c->Invalidate();
+ }
+ }
+
cLuaStateTracker::Del(*this);
lua_close(m_LuaState);
m_LuaState = nullptr;
@@ -402,7 +553,7 @@ bool cLuaState::PushFunction(const char * a_FunctionName)
-bool cLuaState::PushFunction(int a_FnRef)
+bool cLuaState::PushFunction(const cRef & a_FnRef)
{
ASSERT(IsValid());
ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack
@@ -410,7 +561,7 @@ bool cLuaState::PushFunction(int a_FnRef)
// Push the error handler for lua_pcall()
lua_pushcfunction(m_LuaState, &ReportFnCallErrors);
- lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, a_FnRef); // same as lua_getref()
+ 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);
@@ -821,6 +972,18 @@ void cLuaState::Push(std::chrono::milliseconds a_Value)
+void cLuaState::Pop(int a_NumValuesToPop)
+{
+ ASSERT(IsValid());
+
+ lua_pop(m_LuaState, a_NumValuesToPop);
+ m_NumCurrentFunctionArgs -= a_NumValuesToPop;
+}
+
+
+
+
+
bool cLuaState::GetStackValue(int a_StackPos, AString & a_Value)
{
size_t len = 0;
@@ -847,6 +1010,41 @@ bool cLuaState::GetStackValue(int a_StackPos, bool & a_ReturnedVal)
+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, 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))
@@ -1632,16 +1830,16 @@ void cLuaState::ToString(int a_StackPos, AString & a_String)
-void cLuaState::LogStack(const char * a_Header)
+void cLuaState::LogStackValues(const char * a_Header)
{
- LogStack(m_LuaState, a_Header);
+ LogStackValues(m_LuaState, a_Header);
}
-void cLuaState::LogStack(lua_State * a_LuaState, const char * 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
LOG("%s", (a_Header != nullptr) ? a_Header : "Lua C API Stack contents:");
@@ -1667,6 +1865,21 @@ void cLuaState::LogStack(lua_State * a_LuaState, const char * a_Header)
+cLuaState * cLuaState::QueryCanonLuaState(void)
+{
+ // Get the CanonLuaState global from Lua:
+ auto cb = WalkToNamedGlobal(g_CanonLuaStateGlobalName);
+ if (!cb.IsValid())
+ {
+ return nullptr;
+ }
+ return reinterpret_cast<cLuaState *>(lua_touserdata(m_LuaState, -1));
+}
+
+
+
+
+
int cLuaState::ReportFnCallErrors(lua_State * a_LuaState)
{
LOGWARNING("LUA: %s", lua_tostring(a_LuaState, -1));
@@ -1701,6 +1914,50 @@ int cLuaState::BreakIntoDebugger(lua_State * a_LuaState)
+void cLuaState::TrackCallback(cCallback & a_Callback)
+{
+ // 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_CSTrackedCallbacks);
+ canonState->m_TrackedCallbacks.push_back(&a_Callback);
+}
+
+
+
+
+
+void cLuaState::UntrackCallback(cCallback & a_Callback)
+{
+ // 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:
+ cCSLock Lock(canonState->m_CSTrackedCallbacks);
+ auto & trackedCallbacks = canonState->m_TrackedCallbacks;
+ trackedCallbacks.erase(std::remove_if(trackedCallbacks.begin(), trackedCallbacks.end(),
+ [&a_Callback](cCallback * a_StoredCallback)
+ {
+ return (a_StoredCallback == &a_Callback);
+ }
+ ));
+}
+
+
+
+
+
////////////////////////////////////////////////////////////////////////////////
// cLuaState::cRef:
@@ -1756,7 +2013,7 @@ void cLuaState::cRef::RefStack(cLuaState & a_LuaState, int a_StackPos)
{
UnRef();
}
- m_LuaState = &a_LuaState;
+ 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);
}
@@ -1767,11 +2024,9 @@ void cLuaState::cRef::RefStack(cLuaState & a_LuaState, int a_StackPos)
void cLuaState::cRef::UnRef(void)
{
- ASSERT(m_LuaState->IsValid()); // The reference should be destroyed before destroying the LuaState
-
if (IsValid())
{
- luaL_unref(*m_LuaState, LUA_REGISTRYINDEX, m_Ref);
+ luaL_unref(m_LuaState, LUA_REGISTRYINDEX, m_Ref);
}
m_LuaState = nullptr;
m_Ref = LUA_REFNIL;