summaryrefslogtreecommitdiffstats
path: root/src/Bindings
diff options
context:
space:
mode:
Diffstat (limited to 'src/Bindings')
-rw-r--r--src/Bindings/LuaChunkStay.cpp173
-rw-r--r--src/Bindings/LuaChunkStay.h73
-rw-r--r--src/Bindings/LuaState.cpp69
-rw-r--r--src/Bindings/LuaState.h17
-rw-r--r--src/Bindings/ManualBindings.cpp49
-rw-r--r--src/Bindings/PluginLua.h28
6 files changed, 392 insertions, 17 deletions
diff --git a/src/Bindings/LuaChunkStay.cpp b/src/Bindings/LuaChunkStay.cpp
new file mode 100644
index 000000000..0e982637f
--- /dev/null
+++ b/src/Bindings/LuaChunkStay.cpp
@@ -0,0 +1,173 @@
+
+// LuaChunkStay.cpp
+
+// Implements the cLuaChunkStay class representing a cChunkStay binding for plugins, used by cWorld:ChunkStay() Lua API
+
+#include "Globals.h"
+#include "LuaChunkStay.h"
+#include "PluginLua.h"
+#include "../World.h"
+
+
+
+
+
+cLuaChunkStay::cLuaChunkStay(cPluginLua & a_Plugin) :
+ m_Plugin(a_Plugin),
+ m_LuaState(NULL)
+{
+}
+
+
+
+
+
+bool cLuaChunkStay::AddChunks(int a_ChunkCoordTableStackPos)
+{
+ // This function is expected to be called just once, with all the coords in a table
+ ASSERT(m_Chunks.empty());
+
+ cPluginLua::cOperation Op(m_Plugin);
+ cLuaState & L = Op();
+
+ // Check that we got a table:
+ if (!lua_istable(L, a_ChunkCoordTableStackPos))
+ {
+ LOGWARNING("%s: The parameter is not a table of coords (got %s). Ignoring the call.",
+ __FUNCTION__, lua_typename(L, lua_type(L, a_ChunkCoordTableStackPos))
+ );
+ L.LogStackTrace();
+ return false;
+ }
+
+ // Add each set of coords:
+ int NumChunks = luaL_getn(L, a_ChunkCoordTableStackPos);
+ m_Chunks.reserve(NumChunks);
+ for (int idx = 1; idx <= NumChunks; idx++)
+ {
+ // Push the idx-th element of the array onto stack top, check that it's a table:
+ lua_rawgeti(L, a_ChunkCoordTableStackPos, idx);
+ if (!lua_istable(L, -1))
+ {
+ LOGWARNING("%s: Element #%d is not a table (got %s). Ignoring the element.",
+ __FUNCTION__, idx, lua_typename(L, -1)
+ );
+ L.LogStackTrace();
+ lua_pop(L, 1);
+ continue;
+ }
+ AddChunkCoord(L, idx);
+ lua_pop(L, 1);
+ }
+
+ // If there are no chunks, log a warning and return failure:
+ if (m_Chunks.empty())
+ {
+ LOGWARNING("%s: Zero chunks to stay.", __FUNCTION__);
+ L.LogStackTrace();
+ return false;
+ }
+
+ // All ok
+ return true;
+}
+
+
+
+
+
+void cLuaChunkStay::AddChunkCoord(cLuaState & L, int a_Index)
+{
+ // Check that the element has 2 coords:
+ int NumCoords = luaL_getn(L, -1);
+ if (NumCoords != 2)
+ {
+ LOGWARNING("%s: Element #%d doesn't contain 2 coords (got %d). Ignoring the element.",
+ __FUNCTION__, a_Index, NumCoords
+ );
+ return;
+ }
+
+ // Read the two coords from the element:
+ lua_rawgeti(L, -1, 1);
+ lua_rawgeti(L, -2, 2);
+ int ChunkX = luaL_checkint(L, -2);
+ int ChunkZ = luaL_checkint(L, -1);
+ lua_pop(L, 2);
+
+ // Check that a coord is not yet present:
+ for (cChunkCoordsVector::iterator itr = m_Chunks.begin(), end = m_Chunks.end(); itr != end; ++itr)
+ {
+ if ((itr->m_ChunkX == ChunkX) && (itr->m_ChunkZ == ChunkZ))
+ {
+ LOGWARNING("%s: Element #%d is a duplicate, ignoring it.",
+ __FUNCTION__, a_Index
+ );
+ return;
+ }
+ } // for itr - m_Chunks[]
+
+ m_Chunks.push_back(cChunkCoords(ChunkX, ZERO_CHUNK_Y, ChunkZ));
+}
+
+
+
+
+
+void cLuaChunkStay::Enable(cChunkMap & a_ChunkMap, int a_OnChunkAvailableStackPos, int a_OnAllChunksAvailableStackPos)
+{
+ // Get the references to the callback functions:
+ m_LuaState = &m_Plugin.GetLuaState();
+ m_OnChunkAvailable.RefStack(*m_LuaState, a_OnChunkAvailableStackPos);
+ m_OnAllChunksAvailable.RefStack(*m_LuaState, a_OnAllChunksAvailableStackPos);
+
+ // Enable the ChunkStay:
+ super::Enable(a_ChunkMap);
+}
+
+
+
+
+
+void cLuaChunkStay::OnChunkAvailable(int a_ChunkX, int a_ChunkZ)
+{
+ // DEBUG:
+ LOGD("LuaChunkStay: Chunk [%d, %d] is now available, calling the callback...", a_ChunkX, a_ChunkZ);
+
+ cPluginLua::cOperation Op(m_Plugin);
+ Op().Call((int)m_OnChunkAvailable, a_ChunkX, a_ChunkZ);
+}
+
+
+
+
+
+bool cLuaChunkStay::OnAllChunksAvailable(void)
+{
+ {
+ // Call the callback:
+ cPluginLua::cOperation Op(m_Plugin);
+ Op().Call((int)m_OnAllChunksAvailable);
+
+ // Remove the callback references - they won't be needed anymore
+ m_OnChunkAvailable.UnRef();
+ m_OnAllChunksAvailable.UnRef();
+ }
+
+ // Disable the ChunkStay by returning true
+ return true;
+}
+
+
+
+
+
+void cLuaChunkStay::OnDisabled(void)
+{
+ // This object is no longer needed, delete it
+ delete this;
+}
+
+
+
+
diff --git a/src/Bindings/LuaChunkStay.h b/src/Bindings/LuaChunkStay.h
new file mode 100644
index 000000000..49ab9a0ad
--- /dev/null
+++ b/src/Bindings/LuaChunkStay.h
@@ -0,0 +1,73 @@
+
+// LuaChunkStay.h
+
+// Declares the cLuaChunkStay class representing a cChunkStay binding for plugins, used by cWorld:ChunkStay() Lua API
+
+
+
+
+
+#pragma once
+
+#include "LuaState.h"
+#include "../ChunkStay.h"
+
+
+
+
+
+// fwd:
+class cPluginLua;
+
+
+
+
+
+class cLuaChunkStay
+ : public cChunkStay
+{
+ typedef cChunkStay super;
+
+public:
+ cLuaChunkStay(cPluginLua & a_Plugin);
+
+ ~cLuaChunkStay() { }
+
+ /** Adds chunks in the specified on-stack Lua table.
+ Returns true if any chunk added, false (plus log warning) if none. */
+ bool AddChunks(int a_ChunkCoordTableStackPos);
+
+ /** Enables the ChunkStay for the specified chunkmap, with the specified Lua callbacks. */
+ void Enable(cChunkMap & a_ChunkMap, int a_OnChunkAvailableStackPos, int a_OnAllChunksAvailableStackPos);
+
+protected:
+ /** The plugin which has created the ChunkStay, via cWorld:ChunkStay() binding method. */
+ cPluginLua & m_Plugin;
+
+ /** The Lua state associated with the callbacks. Only valid when enabled. */
+ cLuaState * m_LuaState;
+
+ /** The Lua function to call in OnChunkAvailable. Only valid when enabled. */
+ cLuaState::cRef m_OnChunkAvailable;
+
+ /** The Lua function to call in OnAllChunksAvailable. Only valid when enabled. */
+ cLuaState::cRef m_OnAllChunksAvailable;
+
+
+ // cChunkStay overrides:
+ virtual void OnChunkAvailable(int a_ChunkX, int a_ChunkZ) override;
+ virtual bool OnAllChunksAvailable(void) override;
+ virtual void OnDisabled(void) override;
+
+ /** Adds a single chunk coord from the table at the top of the Lua stack.
+ Expects the top element to be a table, checks that it contains two numbers.
+ Uses those two numbers as chunk coords appended to m_Chunks.
+ If the coords are already present, gives a warning and ignores the pair.
+ The a_Index parameter is only for the error messages. */
+ void AddChunkCoord(cLuaState & a_LuaState, int a_Index);
+} ;
+
+
+
+
+
diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp
index ac5ce47e1..24e64d9b2 100644
--- a/src/Bindings/LuaState.cpp
+++ b/src/Bindings/LuaState.cpp
@@ -735,17 +735,20 @@ bool cLuaState::CallFunction(int a_NumResults)
ASSERT(lua_isfunction(m_LuaState, -m_NumCurrentFunctionArgs - 1)); // The function to call
ASSERT(lua_isfunction(m_LuaState, -m_NumCurrentFunctionArgs - 2)); // The error handler
- int s = lua_pcall(m_LuaState, m_NumCurrentFunctionArgs, a_NumResults, -m_NumCurrentFunctionArgs - 2);
+ // 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(), m_CurrentFunctionName.c_str());
- m_NumCurrentFunctionArgs = -1;
- m_CurrentFunctionName.clear();
+ LOGWARNING("Error in %s calling function %s()", m_SubsystemName.c_str(), CurrentFunctionName.c_str());
return false;
}
- m_NumCurrentFunctionArgs = -1;
- m_CurrentFunctionName.clear();
// Remove the error handler from the stack:
lua_remove(m_LuaState, -a_NumResults - 1);
@@ -1206,7 +1209,7 @@ void cLuaState::LogStack(const char * a_Header)
void cLuaState::LogStack(lua_State * a_LuaState, const char * a_Header)
{
LOGD((a_Header != NULL) ? a_Header : "Lua C API Stack contents:");
- for (int i = lua_gettop(a_LuaState); i >= 0; i--)
+ for (int i = lua_gettop(a_LuaState); i > 0; i--)
{
AString Value;
int Type = lua_type(a_LuaState, i);
@@ -1240,13 +1243,21 @@ int cLuaState::ReportFnCallErrors(lua_State * a_LuaState)
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cLuaState::cRef:
+cLuaState::cRef::cRef(void) :
+ m_LuaState(NULL),
+ m_Ref(LUA_REFNIL)
+{
+}
+
+
+
+
+
cLuaState::cRef::cRef(cLuaState & a_LuaState, int a_StackPos) :
- m_LuaState(a_LuaState)
+ m_LuaState(NULL),
+ m_Ref(LUA_REFNIL)
{
- ASSERT(m_LuaState.IsValid());
-
- lua_pushvalue(m_LuaState, a_StackPos); // Push a copy of the value at a_StackPos onto the stack
- m_Ref = luaL_ref(m_LuaState, LUA_REGISTRYINDEX);
+ RefStack(a_LuaState, a_StackPos);
}
@@ -1255,12 +1266,42 @@ cLuaState::cRef::cRef(cLuaState & a_LuaState, int a_StackPos) :
cLuaState::cRef::~cRef()
{
- ASSERT(m_LuaState.IsValid());
+ 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
if (IsValid())
{
- luaL_unref(m_LuaState, LUA_REGISTRYINDEX, m_Ref);
+ luaL_unref(*m_LuaState, LUA_REGISTRYINDEX, m_Ref);
}
+ m_LuaState = NULL;
+ m_Ref = LUA_REFNIL;
}
diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h
index dda45bb28..1c9c99e69 100644
--- a/src/Bindings/LuaState.h
+++ b/src/Bindings/LuaState.h
@@ -65,14 +65,27 @@ class cLuaState
{
public:
- /** Used for storing references to object in the global registry */
+ /** Used for storing references to object in the global registry.
+ Can be bound (contains a reference) or unbound (doesn't contain reference).
+ The reference can also be reset by calling RefStack(). */
class cRef
{
public:
+ /** Creates an unbound reference object. */
+ cRef(void);
+
/** Creates a reference in the specified LuaState for object at the specified StackPos */
cRef(cLuaState & a_LuaState, int a_StackPos);
+
~cRef();
+ /** Creates a reference to Lua object at the specified stack pos, binds this object to it.
+ Calls UnRef() first if previously bound to another reference. */
+ void RefStack(cLuaState & a_LuaState, int a_StackPos);
+
+ /** Removes the bound reference, resets the object to Unbound state. */
+ void UnRef(void);
+
/** Returns true if the reference is valid */
bool IsValid(void) const {return (m_Ref != LUA_REFNIL); }
@@ -80,7 +93,7 @@ public:
operator int(void) const { return m_Ref; }
protected:
- cLuaState & m_LuaState;
+ cLuaState * m_LuaState;
int m_Ref;
} ;
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp
index 841ec5cf2..f0bad92e8 100644
--- a/src/Bindings/ManualBindings.cpp
+++ b/src/Bindings/ManualBindings.cpp
@@ -8,6 +8,7 @@
#include "PluginLua.h"
#include "PluginManager.h"
#include "LuaWindow.h"
+#include "LuaChunkStay.h"
#include "../Root.h"
#include "../World.h"
#include "../Entities/Player.h"
@@ -1638,6 +1639,53 @@ static int tolua_cPluginManager_CallPlugin(lua_State * tolua_S)
+static int tolua_cWorld_ChunkStay(lua_State * tolua_S)
+{
+ /* Function signature:
+ World:ChunkStay(ChunkCoordTable, OnChunkAvailable, OnAllChunksAvailable)
+ ChunkCoordTable == { {Chunk1x, Chunk1z}, {Chunk2x, Chunk2z}, ... }
+ */
+
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserType(1, "cWorld") ||
+ !L.CheckParamTable (2) ||
+ !L.CheckParamFunction(3, 4)
+ )
+ {
+ return 0;
+ }
+
+ cPluginLua * Plugin = GetLuaPlugin(tolua_S);
+ if (Plugin == NULL)
+ {
+ return 0;
+ }
+ cLuaChunkStay * ChunkStay = new cLuaChunkStay(*Plugin);
+
+ // Read the params:
+ cWorld * World = (cWorld *)tolua_tousertype(tolua_S, 1, NULL);
+ if (World == NULL)
+ {
+ LOGWARNING("World:ChunkStay(): invalid world parameter");
+ L.LogStackTrace();
+ return 0;
+ }
+ L.LogStack("Before AddChunks()");
+ if (!ChunkStay->AddChunks(2))
+ {
+ return 0;
+ }
+ L.LogStack("After params read");
+
+ ChunkStay->Enable(*World->GetChunkMap(), 3, 4);
+ return 0;
+}
+
+
+
+
+
static int tolua_cPlayer_GetGroups(lua_State* tolua_S)
{
cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
@@ -2360,6 +2408,7 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cWorld");
+ tolua_function(tolua_S, "ChunkStay", tolua_cWorld_ChunkStay);
tolua_function(tolua_S, "DoWithBlockEntityAt", tolua_DoWithXYZ<cWorld, cBlockEntity, &cWorld::DoWithBlockEntityAt>);
tolua_function(tolua_S, "DoWithChestAt", tolua_DoWithXYZ<cWorld, cChestEntity, &cWorld::DoWithChestAt>);
tolua_function(tolua_S, "DoWithDispenserAt", tolua_DoWithXYZ<cWorld, cDispenserEntity, &cWorld::DoWithDispenserAt>);
diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h
index ad0cfbe5a..a177f5288 100644
--- a/src/Bindings/PluginLua.h
+++ b/src/Bindings/PluginLua.h
@@ -35,7 +35,33 @@ class cPluginLua :
public:
// tolua_end
- cPluginLua( const AString & a_PluginDirectory );
+ /** A RAII-style mutex lock for accessing the internal LuaState.
+ This will be the only way to retrieve the plugin's LuaState;
+ therefore it directly supports accessing the LuaState of the locked plugin.
+ Usage:
+ cPluginLua::cOperation Op(SomePlugin);
+ Op().Call(...) // Call a function in the plugin's LuaState
+ */
+ class cOperation
+ {
+ public:
+ cOperation(cPluginLua & a_Plugin) :
+ m_Plugin(a_Plugin),
+ m_Lock(a_Plugin.m_CriticalSection)
+ {
+ }
+
+ cLuaState & operator ()(void) { return m_Plugin.m_LuaState; }
+
+ protected:
+ cPluginLua & m_Plugin;
+
+ /** RAII lock for m_Plugin.m_CriticalSection */
+ cCSLock m_Lock;
+ } ;
+
+
+ cPluginLua(const AString & a_PluginDirectory);
~cPluginLua();
virtual void OnDisable(void) override;